diff --git a/Cargo.toml b/Cargo.toml new file mode 100644 index 0000000000000000000000000000000000000000..e3a0c400ccef867caabe9013ad2e9cb8c755c599 --- /dev/null +++ b/Cargo.toml @@ -0,0 +1,42 @@ +[package] +name = "ylong_json" +version = "0.1.0" +edition = "2018" +description = "A JSON serialization file format" +#readme = "README.md" +license = "Apache-2.0" +repository = "https://open.codehub.huawei.com/innersource/Ylong_Rust/ylong_rs/files?ref=master&filePath=src%2Flib%2Fparser%2Fylong_json" +keywords = ["ylong", "json", "serialization", "deserialization"] + +# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html + +[lib] +name = "ylong_json" +crate-type = ["cdylib", "staticlib", "lib"] + +[features] +default = ["btree_object", "vec_array"] # Object 默认使用 Btree 结构,Array 默认使用 Vec 结构。 +c_adapter = ["libc"] # 使用 C 封装层接口 +list_array = [] # Array 底层使用 LinkedList。在 Array 的平均子节点数较少(约小于 15 个)、查找数量较少时,性能较好。 +vec_array = [] # Array 底层使用 Vec。在 Array 的平均子节点数较多(约大于 15 个)、查找数量较多时,性能较好。 +list_object = [] # Object 底层使用 LinkedList。在 Object 的平均子节点数较少(约小于 15 个)、查找数量较少时,性能较好。 +vec_object = [] # Object 底层使用 Vec。在 Object 的平均子节点数中等(约大于 15 个,小于 1024 个)、查找数量较少时,性能较好。 +btree_object = [] # Object 底层使用 Btree。在 Object 的平均子节点数较多(约大于 1024 个)、查找数量较多时,性能较好。 +ascii_only = [] # 仅使用 ASCII 字符,正常解析 unicode 字符,但超出 ASCII 的 UTF-8 字符在输出时保持不变。 + +[dependencies] +libc = { version = "0.2.134", optional = true } +serde = { version = "1.0.136", features = ["derive"] } + +[dev-dependencies] +serde_json = "1.0.74" + +[[test]] +name = "sdv_adapter_test" +path = "./tests/sdv_adapter_test.rs" +required-features = ["c_adapter"] + +[[test]] +name = "ylong_json_sdv_test" +path = "./tests/ylong_json_sdv_test.rs" +required-features = [] \ No newline at end of file diff --git a/LICENSE b/LICENSE new file mode 100644 index 0000000000000000000000000000000000000000..261eeb9e9f8b2b4b0d119366dda99c6fd7d35c64 --- /dev/null +++ b/LICENSE @@ -0,0 +1,201 @@ + Apache License + Version 2.0, January 2004 + http://www.apache.org/licenses/ + + TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION + + 1. Definitions. + + "License" shall mean the terms and conditions for use, reproduction, + and distribution as defined by Sections 1 through 9 of this document. + + "Licensor" shall mean the copyright owner or entity authorized by + the copyright owner that is granting the License. + + "Legal Entity" shall mean the union of the acting entity and all + other entities that control, are controlled by, or are under common + control with that entity. For the purposes of this definition, + "control" means (i) the power, direct or indirect, to cause the + direction or management of such entity, whether by contract or + otherwise, or (ii) ownership of fifty percent (50%) or more of the + outstanding shares, or (iii) beneficial ownership of such entity. + + "You" (or "Your") shall mean an individual or Legal Entity + exercising permissions granted by this License. + + "Source" form shall mean the preferred form for making modifications, + including but not limited to software source code, documentation + source, and configuration files. + + "Object" form shall mean any form resulting from mechanical + transformation or translation of a Source form, including but + not limited to compiled object code, generated documentation, + and conversions to other media types. + + "Work" shall mean the work of authorship, whether in Source or + Object form, made available under the License, as indicated by a + copyright notice that is included in or attached to the work + (an example is provided in the Appendix below). + + "Derivative Works" shall mean any work, whether in Source or Object + form, that is based on (or derived from) the Work and for which the + editorial revisions, annotations, elaborations, or other modifications + represent, as a whole, an original work of authorship. For the purposes + of this License, Derivative Works shall not include works that remain + separable from, or merely link (or bind by name) to the interfaces of, + the Work and Derivative Works thereof. + + "Contribution" shall mean any work of authorship, including + the original version of the Work and any modifications or additions + to that Work or Derivative Works thereof, that is intentionally + submitted to Licensor for inclusion in the Work by the copyright owner + or by an individual or Legal Entity authorized to submit on behalf of + the copyright owner. For the purposes of this definition, "submitted" + means any form of electronic, verbal, or written communication sent + to the Licensor or its representatives, including but not limited to + communication on electronic mailing lists, source code control systems, + and issue tracking systems that are managed by, or on behalf of, the + Licensor for the purpose of discussing and improving the Work, but + excluding communication that is conspicuously marked or otherwise + designated in writing by the copyright owner as "Not a Contribution." + + "Contributor" shall mean Licensor and any individual or Legal Entity + on behalf of whom a Contribution has been received by Licensor and + subsequently incorporated within the Work. + + 2. Grant of Copyright License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + copyright license to reproduce, prepare Derivative Works of, + publicly display, publicly perform, sublicense, and distribute the + Work and such Derivative Works in Source or Object form. + + 3. Grant of Patent License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + (except as stated in this section) patent license to make, have made, + use, offer to sell, sell, import, and otherwise transfer the Work, + where such license applies only to those patent claims licensable + by such Contributor that are necessarily infringed by their + Contribution(s) alone or by combination of their Contribution(s) + with the Work to which such Contribution(s) was submitted. If You + institute patent litigation against any entity (including a + cross-claim or counterclaim in a lawsuit) alleging that the Work + or a Contribution incorporated within the Work constitutes direct + or contributory patent infringement, then any patent licenses + granted to You under this License for that Work shall terminate + as of the date such litigation is filed. + + 4. Redistribution. You may reproduce and distribute copies of the + Work or Derivative Works thereof in any medium, with or without + modifications, and in Source or Object form, provided that You + meet the following conditions: + + (a) You must give any other recipients of the Work or + Derivative Works a copy of this License; and + + (b) You must cause any modified files to carry prominent notices + stating that You changed the files; and + + (c) You must retain, in the Source form of any Derivative Works + that You distribute, all copyright, patent, trademark, and + attribution notices from the Source form of the Work, + excluding those notices that do not pertain to any part of + the Derivative Works; and + + (d) If the Work includes a "NOTICE" text file as part of its + distribution, then any Derivative Works that You distribute must + include a readable copy of the attribution notices contained + within such NOTICE file, excluding those notices that do not + pertain to any part of the Derivative Works, in at least one + of the following places: within a NOTICE text file distributed + as part of the Derivative Works; within the Source form or + documentation, if provided along with the Derivative Works; or, + within a display generated by the Derivative Works, if and + wherever such third-party notices normally appear. The contents + of the NOTICE file are for informational purposes only and + do not modify the License. You may add Your own attribution + notices within Derivative Works that You distribute, alongside + or as an addendum to the NOTICE text from the Work, provided + that such additional attribution notices cannot be construed + as modifying the License. + + You may add Your own copyright statement to Your modifications and + may provide additional or different license terms and conditions + for use, reproduction, or distribution of Your modifications, or + for any such Derivative Works as a whole, provided Your use, + reproduction, and distribution of the Work otherwise complies with + the conditions stated in this License. + + 5. Submission of Contributions. Unless You explicitly state otherwise, + any Contribution intentionally submitted for inclusion in the Work + by You to the Licensor shall be under the terms and conditions of + this License, without any additional terms or conditions. + Notwithstanding the above, nothing herein shall supersede or modify + the terms of any separate license agreement you may have executed + with Licensor regarding such Contributions. + + 6. Trademarks. This License does not grant permission to use the trade + names, trademarks, service marks, or product names of the Licensor, + except as required for reasonable and customary use in describing the + origin of the Work and reproducing the content of the NOTICE file. + + 7. Disclaimer of Warranty. Unless required by applicable law or + agreed to in writing, Licensor provides the Work (and each + Contributor provides its Contributions) on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or + implied, including, without limitation, any warranties or conditions + of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A + PARTICULAR PURPOSE. You are solely responsible for determining the + appropriateness of using or redistributing the Work and assume any + risks associated with Your exercise of permissions under this License. + + 8. Limitation of Liability. In no event and under no legal theory, + whether in tort (including negligence), contract, or otherwise, + unless required by applicable law (such as deliberate and grossly + negligent acts) or agreed to in writing, shall any Contributor be + liable to You for damages, including any direct, indirect, special, + incidental, or consequential damages of any character arising as a + result of this License or out of the use or inability to use the + Work (including but not limited to damages for loss of goodwill, + work stoppage, computer failure or malfunction, or any and all + other commercial damages or losses), even if such Contributor + has been advised of the possibility of such damages. + + 9. Accepting Warranty or Additional Liability. While redistributing + the Work or Derivative Works thereof, You may choose to offer, + and charge a fee for, acceptance of support, warranty, indemnity, + or other liability obligations and/or rights consistent with this + License. However, in accepting such obligations, You may act only + on Your own behalf and on Your sole responsibility, not on behalf + of any other Contributor, and only if You agree to indemnify, + defend, and hold each Contributor harmless for any liability + incurred by, or claims asserted against, such Contributor by reason + of your accepting any such warranty or additional liability. + + END OF TERMS AND CONDITIONS + + APPENDIX: How to apply the Apache License to your work. + + To apply the Apache License to your work, attach the following + boilerplate notice, with the fields enclosed by brackets "[]" + replaced with your own identifying information. (Don't include + the brackets!) The text should be enclosed in the appropriate + comment syntax for the file format. We also recommend that a + file or class name and description of purpose be included on the + same "printed page" as the copyright notice for easier + identification within third-party archives. + + Copyright [yyyy] [name of copyright owner] + + Licensed 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. diff --git a/OAT.xml b/OAT.xml new file mode 100644 index 0000000000000000000000000000000000000000..d09d3be7810c866745a4dea23919cdc2639097ea --- /dev/null +++ b/OAT.xml @@ -0,0 +1,71 @@ + + + + + + + + + + + + + + + + + + diff --git a/README.en.md b/README.en.md deleted file mode 100644 index b7ad888eb7e41fa4ba66b2ea88376150bf474f08..0000000000000000000000000000000000000000 --- a/README.en.md +++ /dev/null @@ -1,36 +0,0 @@ -# commonlibrary_rust_ylong_json - -#### Description -{**When you're done, you can delete the content in this README and update the file with details for others getting started with your repository**} - -#### Software Architecture -Software architecture description - -#### Installation - -1. xxxx -2. xxxx -3. xxxx - -#### Instructions - -1. xxxx -2. xxxx -3. xxxx - -#### Contribution - -1. Fork the repository -2. Create Feat_xxx branch -3. Commit your code -4. Create Pull Request - - -#### Gitee Feature - -1. You can use Readme\_XXX.md to support different languages, such as Readme\_en.md, Readme\_zh.md -2. Gitee blog [blog.gitee.com](https://blog.gitee.com) -3. Explore open source project [https://gitee.com/explore](https://gitee.com/explore) -4. The most valuable open source project [GVP](https://gitee.com/gvp) -5. The manual of Gitee [https://gitee.com/help](https://gitee.com/help) -6. The most popular members [https://gitee.com/gitee-stars/](https://gitee.com/gitee-stars/) diff --git a/README.md b/README.md index 92e172385b7a5d5bea31b5ad9d3eb63a2a16c6fa..bee32da03612426153125cb68e35b5e0e4b8367f 100644 --- a/README.md +++ b/README.md @@ -1,39 +1,296 @@ -# commonlibrary_rust_ylong_json +# ylong_json -#### 介绍 -{**以下是 Gitee 平台说明,您可以替换此简介** -Gitee 是 OSCHINA 推出的基于 Git 的代码托管平台(同时支持 SVN)。专为开发者提供稳定、高效、安全的云端软件开发协作平台 -无论是个人、团队、或是企业,都能够用 Gitee 实现代码托管、项目管理、协作开发。企业项目请看 [https://gitee.com/enterprises](https://gitee.com/enterprises)} +### 简介 -#### 软件架构 -软件架构说明 +`ylong_json` 模块提供了 JSON 语法格式文本或字符串的序列化功能,以及对应生成实例的反序列化功能。 +`ylong_json` 包含以下核心功能: -#### 安装教程 +##### 功能一:生成 JSON 实例 +`ylong_json` 提供了从 JSON 文本或字符串生成一个 `JsonValue` 实例的功能。 -1. xxxx -2. xxxx -3. xxxx +(1)可以通过以下方法创建 `JsonValue` 实例: +```rust +use std::fs::File; +use std::str::FromStr; +use std::io::Read; +use ylong_json::JsonValue; -#### 使用说明 +fn create_json_value_instance() { + let str: &str = ""; + // 可以使用 from_str 接口,从 &str 类型尝试生成 JsonValue 实例。 + // 如果传入的 &str 不符合 JSON 语法,会返回对应的 Error。 + let json_value = JsonValue::from_str(str); + + let text: String = String::from(""); + // 可以使用 from_text 接口,从一系列实现 AsRef<[u8]> 的类型生成 JsonValue 实例。 + // 如果传入的文本内容不符合 JSON 语法,会返回对应的 Error。 + let json_value = JsonValue::from_text(text); + + let path: &str = ""; + // 可以使用 from_file 接口,从对应路径的文件读取内容,并尝试生成 JsonValue 实例。 + // 如果传入的 path 不合法或者文本内容不符合 JSON 语法,会返回对应的 Error。 + let json_value = JsonValue::from_file(path); + + let mut reader: Box = Box::new(File::open("").unwrap()); + // 可以使用 from_reader 接口,从实现了 io::Read 的实例中读取文本,并尝试生成 JsonValue 实例。 + // 如果读取失败或者从 reader 中读取的内容不符合 JSON 语法,会返回对应的 Error。 + let json_value = JsonValue::from_reader(&mut reader); +} +``` +当 `JsonValue` 实例创建成功后,就可以尝试读取和修改对应的内容了。 -1. xxxx -2. xxxx -3. xxxx +(2)如果 JSON 文本中的类型实现了第三方库 `serde::Deserialize` trait,则可以直接将文本内容反序列化为该类型的实例。 +```rust +use std::fs::File; +use serde::Deserialize; +use ylong_json::deserializer::{from_reader, from_slice, from_st}; +fn deserialize_json_to_instance() { + #[derive(Deserialize, PartialEq, Debug)] + struct Example { + int: u32, + seq: Vec, + tup: (i32, i32, i32), + } -#### 参与贡献 + // 可以使用 from_str 接口,从 &str 类型生成实例。 + // 如果传入的 &str 不符合 JSON 语法,会返回对应的 Error。 + let tr = r#"{"int":1,"seq":["abcd","efgh"],"tup":[1,2,3]}"#; + let example = from_str::(str).unwrap(); -1. Fork 本仓库 -2. 新建 Feat_xxx 分支 -3. 提交代码 -4. 新建 Pull Request + // 可以使用 from_slice 接口,从 &u8 类型生成实例。 + // 如果传入的 &u8 不符合 JSON 语法,会返回对应的 Error。 + let slice = str.as_bytes(); + let example = from_slice::(slice).unwrap(); + + // 可以使用 from_reader 接口,从实现了 io::Write 的位置、文件、io流等生成实例。 + // 如果传入的文本内容不符合 JSON 语法,会返回对应的 Error。 + let mut file: File = File::open("./example.txt").unwrap(); + let example = from_reader::(file).unwrap(); +} +``` -#### 特技 +##### 功能二:读取、修改键值对 +`JsonValue` 实例生成成功后,可以通过各种下标来查找对应的键值对(获取到对应 `JsonValue` 的普通引用)。 -1. 使用 Readme\_XXX.md 来支持不同的语言,例如 Readme\_en.md, Readme\_zh.md -2. Gitee 官方博客 [blog.gitee.com](https://blog.gitee.com) -3. 你可以 [https://gitee.com/explore](https://gitee.com/explore) 这个地址来了解 Gitee 上的优秀开源项目 -4. [GVP](https://gitee.com/gvp) 全称是 Gitee 最有价值开源项目,是综合评定出的优秀开源项目 -5. Gitee 官方提供的使用手册 [https://gitee.com/help](https://gitee.com/help) -6. Gitee 封面人物是一档用来展示 Gitee 会员风采的栏目 [https://gitee.com/gitee-stars/](https://gitee.com/gitee-stars/) +&str 和 String 类型的下标可以用于查找 Object 内的键值对;usize 类型的下标可以用于查找 Array 内的键值对。 +```rust +use std::str::FromStr; +use ylong_json::JsonValue; + +// 示例的 JSON 字符串 +const JSON_TEXT: &str = r#" +{ + "key": "value", + "array": [1, 2, 3] +} +"#; + +fn find_key_value_pair() { + // 根据示例字符串创建 JsonValue 实例,语法正确所以此处解析必定成功,使用 unwrap。 + let json_value = JsonValue::from_str(JSON_TEXT).unwrap(); + + // 由于 json 本身也是一个表,所以可以使用 &str 类型获取内部值的普通引用。 + let value: &JsonValue = &json_value["key"]; + + // 可以通过 &str 类型先获取到 “array” 成员的普通引用,再根据 usize 类型获取对应元素的普通引用。 + let array_item: &JsonValue = &json_value["array"][0]; + + // 如果尝试查找一个不存在表中的键,会返回 &JsonValue::Null。 + let no_such_key: &JsonValue = &json_value["no_such_key"]; + + // 对 Array 类型查找时,若下标超过 Array 长度,也会返回 &JsonValue::Null。 + let no_such_index: &JsonValue = &json_value["array"][100]; + + // 对一个 Object 和 Array 类型以外的 JsonValue 类型使用下标访问也会返回 &JsonValue::Null。 + let invalid_index: &JsonValue = &json_value["key"]["invalid"]; + let invalid_index: &JsonValue = &json_value["key"][0]; +} +``` +也可以通过相同方法获取到对应 `JsonValue` 的可变引用,获取到可变引用后可以对其进行修改,修改时需要注意符合 JSON 语法。 +```rust +use ylong_json::JsonValue; + +// 示例的 JSON 字符串 +const JSON_TEXT: &str = r#" +{ + "key": "value", + "array": [1, 2, 3] +} +"#; + +fn modify_key_value_pair() { + // 根据示例字符串创建 JsonValue 实例,语法正确所以此处解析必定成功,使用 unwrap。 + // 此处由于需要获取可变引用,JSON 实例需要可变。 + let mut json_value = JsonValue::from_str(JSON_TEXT).unwrap(); + + // 通过 “key” 获取到对应成员的可变引用,并将其设置为数值 123。 + // 库中给许多基本类型实现了从自身到 JsonValue 的转换,所以可以通过 into() 方法转换为 JsonValue。 + // 执行此句代码后,表中内容如下: + // { + // "key": 123, + // "array": [1, 2, 3] + // } + json_value["key"] = 123_i32.into(); + + // 通过 “array” 和下标 0 获取到对应成员的可变引用,并将其设置为数值 123。 + // 执行此句代码后,表中内容如下: + // { + // "key": 123, + // "array": [123, 2, 3] + // } + json_value["array"][0] = 123_i32.into(); + + // 如果尝试获取一个不存在表中的键的可变引用,会在表中插入该键且对应值为 JsonValue::Null,并在此基础上进行修改。 + // 执行此行代码后,json_value 中会增加一个成员 “no_such_key”,且值为数值 123。 + // 表中内容如下: + // { + // "key": 123, + // "array": [123, 2, 3], + // "no_such_key": 123 + // } + json_value["no_such_key"] = 123_i32.into(); + + // 对 Array 类型的成员尝试获取可变引用时,若下标超过 Array 长度, + // 会在 Array 末尾插入一个 JsonValue::Null,并返回该位置的可变引用。 + // 执行此行代码后,json_value 的 “array” 成员的长度变为 4。 + // 表中内容如下: + // { + // "key": 123, + // "array": [123, 2, 3, 123], + // "no_such_key": 123 + // } + json_value["array"][100] = 123_i32.into(); + + // 对一个非 Object 类型使用 &str 类型或 String 下标获取可变引用时, + // 会将该值替换为一个空 Object,然后再用此下标对其进行访问。 + // 执行此代码后,json_value 的 array 成员变成 Object 类型,且含有一个键值对:“key” => 123。 + // 表中内容如下: + // { + // "key": 123, + // "array": { + // "key": 123 + // }, + // "no_such_key": 123 + // } + json_value["array"]["key"] = 123_i32.into(); + + // 对一个非 Array 类型使用 usize 类型下标获取可变引用时, + // 会将该值替换成一个空 Array,然后再用此下标对其进行访问。 + // 执行此代码后,json_value 的 key 成员变成 Array 类型,且含有一个成员: key[0] => 123 + // 表中内容如下: + // { + // "key": [123], + // "array": { + // "key": 123 + // }, + // "no_such_key": 123 + // } + json_value["key"][0] = 123_i32.into(); +} +``` + +##### 功能三:输出 JSON 文本 +(1)当拥有一个 `JsonValue` 实例时,可以将该 `JsonValue` 实例转化成文本并输出到指定位置:字符串、文件、网络等。 +```rust +use std::fs::File; +use ylong_json::JsonValue; + +fn output_json_text(json_value: JsonValue) { + // 使用 to_compact_string() 接口将 json_value 输出成一个字符串。 + let string = json_value.to_compact_string().unwrap(); + + // 使用 compact_encode() 接口将 JSON 文本输出到指定实现了 io::Write 的位置,文件、io流等。 + let mut file: File = File::open("").unwrap(); + let _ = json_value.compact_encode(&mut file); +} +``` +由于 JSON 内部元素没有较强的顺序要求,所以成员的输出顺序会有一定随机性,但是不影响 JSON 文本的语义。 + +(2)可以将一个实现了第三方库 `serde::Serialize` trait 的类型实例序列化为 JSON 文本。 +```rust +use std::fs::File; +use serde::Serialize; +use ylong_json::serializer_compact::{to_string, to_writer}; + +fn output_json_text() { + #[derive(Serialize)] + struct Exmaple { + int: u32, + seq: Vec<&'static str>, + tup: (i32, i32, i32), + } + + let example = Example { + int: 1, + seq: vec!["a", "b"], + tup: (1, 2, 3), + }; + + // 使用 to_string() 接口将 value 输出成一个字符串。 + let string = to_string(&example).unwrap(); + + // 使用 to_writer() 接口将 JSON 文本输出到指定实现了 io::Write 的位置,文件、io流等。 + let mut file: File = File::open("./example.txt").unwrap(); + let _ = to_writer(&example, &mut file); +} +``` + +### 性能测试 +``` +1.测试环境 +操作系统:Linux +架构:x86_64 +字节序:小端 +CPU 型号:Intel(R) Xeon(R) Gold 6278C CPU @ 2.60GHz +CPU 核心数:8 +内存:16G + +2.测试结果 +| 序列化 | ylong_json | serde_json | +----------------------------------------------- +| null | 150 ns/iter | 175 ns/iter | +| boolean | 155 ns/iter | 178 ns/iter | +| number | 309 ns/iter | 291 ns/iter | +| string | 513 ns/iter | 413 ns/iter | +| array | 998 ns/iter | 1,075 ns/iter | +| object | 1,333 ns/iter | 1,348 ns/iter | +| example1 | 12,537 ns/iter | 12,288 ns/iter | +| example2 | 23,754 ns/iter | 21,936 ns/iter | +| example3 | 103,061 ns/iter | 97,247 ns/iter | +| example4 | 15,234 ns/iter | 17,895 ns/iter | + +| 反序列化 | ylong_json | serde_json | +----------------------------------------------- +| null | 257 ns/iter | 399 ns/iter | +| boolean | 260 ns/iter | 400 ns/iter | +| number | 1,507 ns/iter | 989 ns/iter | +| string | 414 ns/iter | 610 ns/iter | +| array | 2,258 ns/iter | 2,148 ns/iter | +| object | 810 ns/iter | 1,386 ns/iter | +| example1 | 10,191 ns/iter | 10,227 ns/iter | +| example2 | 15,753 ns/iter | 18,022 ns/iter | +| example3 | 55,910 ns/iter | 59,717 ns/iter | +| example4 | 18,461 ns/iter | 12,471 ns/iter | +``` + +### 目录 + +``` +ylong_json +├─ examples # ylong_json 代码示例 +├─ include # ylong_json.h +├─ src +│ ├─ value # Array, Object 类型定义和相关方法实现 +│ ├─ adapter.rs # 适配 C 的接口实现 +│ ├─ consts.rs # 一些常数与表格的定义 +│ ├─ deserializer.rs # 适配 serde 的反序列化实现 +│ ├─ encoder.rs # 为 JsonValue 类型序列化实现 +│ ├─ error.rs # 错误类型定义,便于定位 +│ ├─ link_list.rs # LinkedList 类型定义和相关方法实现 +│ ├─ serializer_compact.rs # 适配 serde 的序列化实现 +│ ├─ states.rs # 为 JsonValue 类型反序列化实现 +│ └─ value.rs # JsonValue 类型定义和相关方法实现 +└─ tests # 测试目录 +``` \ No newline at end of file diff --git a/README_EN.md b/README_EN.md new file mode 100644 index 0000000000000000000000000000000000000000..5ce124f223f16205d97146c6645005638af73654 --- /dev/null +++ b/README_EN.md @@ -0,0 +1,328 @@ +# ylong_json + +### Introduction + +The `ylong_json` module provides serialization of text or string in JSON syntax format and deserialization of corresponding generated instances. + +`ylong_json` contains the following core functionality: + +##### Function 1: Generates a JSON instance +`ylong_json` provides the ability to generate an instance of `JsonValue` from JSON text or string. You need to use a series of instance creation methods for the "JsonValue" to use this feature. + +(1) You can create a `JsonValue` instance by: +```rust +use std::fs::File; +use std::str::FromStr; +use std::io::Read; +use ylong_json::JsonValue; +fn create_json_value_instance() { + let str: &str = ""; + // You can use `from_str` to try to generate a `JsonValue` instance from + // the &str type. + // If the passed &str does not conform to JSON syntax, the corresponding + // Error will be returned. + let json_value = JsonValue::from_str(str); + + let text: String = String::from(""); + // You can use `from_text` to generate a `JsonValue` instance from + // a series of types that implement AsRef<[u8]>. + // If the passed text content does not conform to JSON syntax, the + // corresponding Error will be returned. + let json_value = JsonValue::from_text(text); + + let path: &str = ""; + // You can use `from_file` to read a file from corresponding path and + // try to generate a `JsonValue` instance. + // If the passed path is not valid or the text content does not conform + // to JSON syntax, the corresponding Error will be returned. + let json_value = JsonValue::from_file(path); + + let mut reader: Box = Box::new(File::open("").unwrap()); + // You can use `from_reader` interface to read text from an instance + // that implements io::Read and try to generate a `JsonValue` instance. + // If the read fails or if the content from the reader does not conform + // to JSON syntax, the corresponding Error will be returned. + let json_value = JsonValue::from_reader(&mut reader); +} +``` +Once the `JsonValue` instance has been successfully created, you can attempt to read and modify the corresponding contents. + +(2) If the type in the JSON text implements the third-party library `serde::Deserialize` trait, you can directly deserialize the text content to an instance of that type. +```rust +use std::fs::File; +use serde::Deserialize; +use ylong_json::deserializer::{from_reader, from_slice, from_st}; +fn deserialize_json_to_instance() { + #[derive(Deserialize, PartialEq, Debug)] + struct Example { + int: u32, + seq: Vec, + tup: (i32, i32, i32), + } + + // You can use `from_str` to try to generate an instance from String. + // If the passed String does not conform to JSON syntax, the corresponding + // Error will be returned. + let str = r#"{"int":1,"seq":["abcd","efgh"],"tup":[1,2,3]}"#; + let example = from_str::(str).unwrap(); + + // You can use `from_slice` to try to generate an instance from &u8. + // If the passed &u8 does not conform to JSON syntax, the corresponding + // Error will be returned. + let slice = str.as_bytes(); + let example = from_slice::(slice).unwrap(); + + // You can use `from_reader` to try to generate an instance from + // locations, files, io streams, and so on that implement io::Write. + // If the passed text content does not conform to JSON syntax, + // the corresponding Error will be returned. + let mut file: File = File::open("./example.txt").unwrap(); + let example = from_reader::(file).unwrap(); +} +``` + +##### Function 2: Reads and modifies a key-value pair +After a `JsonValue` instance is successfully generated, you can use a subscript to find the corresponding key-value pair (to obtain a common reference to the corresponding `JsonValue`). + +A subscript of type &str or String can be used to find a key-value pair in Object; +A Subscript of type usize can be used to find a key-value pair in an Array. +```rust +use std::str::FromStr; +use ylong_json::JsonValue; + +// JSON string for the example +const JSON_TEXT: &str = r#" +{ + "key": "value", + "array": [1, 2, 3] +} +"#; + +fn find_key_value_pair() { + // Creates a JsonValue instance from the example string, the syntax is + // correct so the parse must succeed here, so uses unwrap. + let json_value = JsonValue::from_str(JSON_TEXT).unwrap(); + + // Since json is itself a table, you can use the &str type to obtain + // a common reference to the internal value. + let value: &JsonValue = &json_value["key"]; + + // You can use the &str type to obtain a common reference to the "array" member, and + // then use the usize type to obtain a common reference to the corresponding element. + let array_item: &JsonValue = &json_value["array"][0]; + + // If you try to find a key that does not exist in a table, + // `&JsonValue::Null` will be returned. + let no_such_key: &JsonValue = &json_value["no_such_key"]; + + // When searching for the Array type, if the subscript exceeds the Array length, + // `&JsonValue::Null` will also be returned. + let no_such_index: &JsonValue = &json_value["array"][100]; + + // If you use a subscript to visit `JsonValue` types other than Object and Array, + // `&JsonValue::Null` will also be returned. + let invalid_index: &JsonValue = &json_value["key"]["invalid"]; + let invalid_index: &JsonValue = &json_value["key"][0]; +} +``` +You can also use the same method to obtain a mutable reference to `JsonValue`. +After obtaining the mutable reference, you can modify it, but you need to make sure that it conforms to JSON syntax. +```rust +use ylong_json::JsonValue; + +// JSON string for the example +const JSON_TEXT: &str = r#" +{ + "key": "value", + "array": [1, 2, 3] +} +"#; + +fn modify_key_value_pair() { + // Creates a JsonValue instance from the example string, the syntax is + // correct so the parse must succeed here, so uses unwrap. + // Here the JSON instance needs to be mutable because you need to obtain a mutable reference. + let mut json_value = JsonValue::from_str(JSON_TEXT).unwrap(); + + // Obtains a mutable reference to the member by "key" and set it to the number 123. + // In the libraty, many primitive types implement conversion from themselves to JsonValue, + // so they can be converted to `JsonValue` by using `into()` method. + // After executing this code, the contents of the table are as follows: + // { + // "key": 123, + // "array": [1, 2, 3] + // } + json_value["key"] = 123_i32.into(); + + // Obtains a mutable reference to the member by using "array" and the subscript 0, + // and set it to the number 123. + // After executing this code, the contents of the table are as follows: + // { + // "key": 123, + // "array": [123, 2, 3] + // } + json_value["array"][0] = 123_i32.into(); + + // If you try to obtain a mutable reference to a key that does not exist in the table, + // then the key will be inserted in the table with the corresponding value JsonValue::Null, + // and changes the value baesd on that. + // After executing this code, the json_value member "no_such_key" has been added, + // and the value is 123. + // The contents of the table are as follows: + // { + // "key": 123, + // "array": [123, 2, 3], + // "no_such_key": 123 + // } + json_value["no_such_key"] = 123_i32.into(); + + // When trying to obtain a mutable reference to a member of the Array type, if the + // subscript exceeds the Array length, then a `JsonValue::Null` will be added at + // the end of the Array and will return a mutable reference to that position. + // After executing this code, the length of the array member of `json_value` becomes 4, + // and the value of the last member is 123. + // The contents of the table are as follows: + // { + // "key": 123, + // "array": [123, 2, 3, 123], + // "no_such_key": 123 + // } + json_value["array"][100] = 123_i32.into(); + + // When using a subscript of &str type or String type to obtain a mutable reference to + // a non-Object type, will replace the value with an empty Object and then visit it with + // that subscript. + // After executing this code, the array member of `json_value` becomes of type Object + // and contains a key-value pair: "key" => 123. + // The contents of the table are as follows: + // { + // "key": 123, + // "array": { + // "key": 123 + // }, + // "no_such_key": 123 + // } + json_value["array"]["key"] = 123_i32.into(); + + // When using a subscript of usize type to obtain a mutable reference to a non-Array + // type, will replace the value with an empty Array and then visit it with that subscript. + // After executing this code, the key member of `json_value` becomes of type Array, + // and contains a member: key[0] => 123. + // The contents of the table are as follows: + // { + // "key": [123], + // "array": { + // "key": 123 + // }, + // "no_such_key": 123 + // } + json_value["key"][0] = 123_i32.into(); +} +``` + +##### Function 3: Outputs JSON text +(1) When you have a JsonValue instance, you can convert it to text and output it to a specified location: string, file, network, etc. +```rust +use std::fs::File; +use ylong_json::JsonValue; + +fn output_json_text(json_value: JsonValue) { + // Uses `to_compact_string()` to output the `json_value` as a string. + let string = json_value.to_compact_string().unwrap(); + + // Uses `compact_encode()` to output JSON text to a specified location, + // file, io stream, etc., which implements io::Write. + let mut file: File = File::open("").unwrap(); + let _ = json_value.compact_encode(&mut file); +} +``` +Because there is no strong order requirement for JSON internal elements, +the output order of members will have a certain randomness, +but it does not affect the semantics of JSON text. + +(2) You can also serialize an instance of a type that implements the `serde::Serialize` trait to JSON text. +```rust +use std::fs::File; +use serde::Serialize; +use ylong_json::serializer_compact::{to_string, to_writer}; + +fn output_json_text(value: V) { + #[derive(Serialize)] + struct Exmaple { + int: u32, + seq: Vec<&'static str>, + tup: (i32, i32, i32), + } + + let example = Example { + int: 1, + seq: vec!["a", "b"], + tup: (1, 2, 3), + }; + + // Uses `to_string()` to output the value as a string. + let string = to_string(&example).unwrap(); + + // Uses `to_writer()` to output JSON text to a specified location, + // file, io stream, etc., which implements io::Write. + let mut file: File = File::open("./example.txt").unwrap(); + let _ = to_writer(&example, &mut file); +} +``` + +### Performance test +``` +1.Test environment +OS: Linux +Architecture: x86_64 +Byte Order: Little Endian +Model number: Intel(R) Xeon(R) Gold 6278C CPU @ 2.60GHz +CPU(s): 8 +MemTotal: 16G + +2.Test result +| Serialize | ylong_json | serde_json | +------------------------------------------------ +| null | 150 ns/iter | 175 ns/iter | +| boolean | 155 ns/iter | 178 ns/iter | +| number | 309 ns/iter | 291 ns/iter | +| string | 513 ns/iter | 413 ns/iter | +| array | 998 ns/iter | 1,075 ns/iter | +| object | 1,333 ns/iter | 1,348 ns/iter | +| example1 | 12,537 ns/iter | 12,288 ns/iter | +| example2 | 23,754 ns/iter | 21,936 ns/iter | +| example3 | 103,061 ns/iter | 97,247 ns/iter | +| example4 | 15,234 ns/iter | 17,895 ns/iter | + +| Deserialize | ylong_json | serde_json | +-------------------------------------------------- +| null | 257 ns/iter | 399 ns/iter | +| boolean | 260 ns/iter | 400 ns/iter | +| number | 1,507 ns/iter | 989 ns/iter | +| string | 414 ns/iter | 610 ns/iter | +| array | 2,258 ns/iter | 2,148 ns/iter | +| object | 810 ns/iter | 1,386 ns/iter | +| example1 | 10,191 ns/iter | 10,227 ns/iter | +| example2 | 15,753 ns/iter | 18,022 ns/iter | +| example3 | 55,910 ns/iter | 59,717 ns/iter | +| example4 | 18,461 ns/iter | 12,471 ns/iter | +``` + +### Directory +``` +ylong_json +├─ examples # ylong_json code example +├─ include # ylong_json.h +├─ src +│ ├─ value # Array and Object type definitions and related methods +│ ├─ adapter.rs # Adapts to the C interface implementation +│ ├─ consts.rs # Some definitions of constants and tables +│ ├─ deserializer.rs # Deserialization implementation of the adaptation serde +│ ├─ encoder.rs # Serialization implementation for the `JsonValue` type +│ ├─ error.rs # Error type definition, helpful to identify the problem +│ ├─ link_list.rs # LinkedList type definition and related methods +│ ├─ serializer_compact.rs # Serialization implementation of the adaptation serde +│ ├─ states.rs # Deserialization implementation for the `JsonValue` type +│ └─ value.rs # JsonValue type definition and related methods +└─ tests # Test directory +``` \ No newline at end of file diff --git a/benches/deserialize_json_cmp.rs b/benches/deserialize_json_cmp.rs new file mode 100644 index 0000000000000000000000000000000000000000..4b3f6500ee83bee49546a5b748788c44cfca641c --- /dev/null +++ b/benches/deserialize_json_cmp.rs @@ -0,0 +1,211 @@ +/* + * Copyright (c) 2023 Huawei Device Co., Ltd. + * Licensed 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. + */ + +//! Benchmarks for the json deserialization + +#![feature(test)] + +mod task_helpers; + +#[cfg(test)] +mod serialize_cmp { + extern crate test; + use crate::task_helpers::*; + use test::Bencher; + + use serde_json::Value; + use std::str::FromStr; + use ylong_json::JsonValue; + + #[bench] + fn null_deserialize_perf_ylong_json(b: &mut Bencher) { + b.iter(|| { + for _ in 0..LOOPS_NUM { + let _value = JsonValue::from_str(NULL_EXAMPLE).unwrap(); + } + }); + } + + #[bench] + fn boolean_deserialize_perf_ylong_json(b: &mut Bencher) { + b.iter(|| { + for _ in 0..LOOPS_NUM { + let _value = JsonValue::from_str(BOOLEAN_EXAMPLE).unwrap(); + } + }); + } + + #[bench] + fn number_deserialize_perf_ylong_json(b: &mut Bencher) { + b.iter(|| { + for _ in 0..LOOPS_NUM { + let _value = JsonValue::from_str(NUMBER_EXAMPLE).unwrap(); + } + }); + } + + #[bench] + fn string_deserialize_perf_ylong_json(b: &mut Bencher) { + b.iter(|| { + for _ in 0..LOOPS_NUM { + let _value = JsonValue::from_str(STRING_EXAMPLE).unwrap(); + } + }); + } + + #[bench] + fn array_deserialize_perf_ylong_json(b: &mut Bencher) { + b.iter(|| { + for _ in 0..LOOPS_NUM { + let _value = JsonValue::from_str(ARRAY_EXAMPLE).unwrap(); + } + }); + } + + #[bench] + fn object_deserialize_perf_ylong_json(b: &mut Bencher) { + b.iter(|| { + for _ in 0..LOOPS_NUM { + let _value = JsonValue::from_str(OBJECT_EXAMPLE).unwrap(); + } + }); + } + + #[bench] + fn exp1_deserialize_perf_ylong_json(b: &mut Bencher) { + b.iter(|| { + for _ in 0..LOOPS_NUM { + let _value = JsonValue::from_str(RFC7159_EXAMPLE1).unwrap(); + } + }); + } + + #[bench] + fn exp2_deserialize_perf_ylong_json(b: &mut Bencher) { + b.iter(|| { + for _ in 0..LOOPS_NUM { + let _value = JsonValue::from_str(RFC7159_EXAMPLE2).unwrap(); + } + }); + } + + #[bench] + fn exp3_deserialize_perf_ylong_json(b: &mut Bencher) { + b.iter(|| { + for _ in 0..LOOPS_NUM { + let _value = JsonValue::from_str(JSON_PARSE_TEST).unwrap(); + } + }); + } + + #[bench] + fn exp4_deserialize_perf_ylong_json(b: &mut Bencher) { + b.iter(|| { + for _ in 0..LOOPS_NUM { + let _value = JsonValue::from_str(LONG_KEY_VALUE).unwrap(); + } + }); + } + + #[bench] + fn null_deserialize_perf_serde_json(b: &mut Bencher) { + b.iter(|| { + for _ in 0..LOOPS_NUM { + let _value: Value = serde_json::from_str(NULL_EXAMPLE).unwrap(); + } + }); + } + + #[bench] + fn boolean_deserialize_perf_serde_json(b: &mut Bencher) { + b.iter(|| { + for _ in 0..LOOPS_NUM { + let _value: Value = serde_json::from_str(BOOLEAN_EXAMPLE).unwrap(); + } + }); + } + + #[bench] + fn number_deserialize_perf_serde_json(b: &mut Bencher) { + b.iter(|| { + for _ in 0..LOOPS_NUM { + let _value: Value = serde_json::from_str(NUMBER_EXAMPLE).unwrap(); + } + }); + } + + #[bench] + fn string_deserialize_perf_serde_json(b: &mut Bencher) { + b.iter(|| { + for _ in 0..LOOPS_NUM { + let _value: Value = serde_json::from_str(STRING_EXAMPLE).unwrap(); + } + }); + } + + #[bench] + fn array_deserialize_perf_serde_json(b: &mut Bencher) { + b.iter(|| { + for _ in 0..LOOPS_NUM { + let _value: Value = serde_json::from_str(ARRAY_EXAMPLE).unwrap(); + } + }); + } + + #[bench] + fn object_deserialize_perf_serde_json(b: &mut Bencher) { + b.iter(|| { + for _ in 0..LOOPS_NUM { + let _value: Value = serde_json::from_str(OBJECT_EXAMPLE).unwrap(); + } + }); + } + + #[bench] + fn exp1_deserialize_perf_serde_json(b: &mut Bencher) { + b.iter(|| { + for _ in 0..LOOPS_NUM { + let _value: Value = serde_json::from_str(RFC7159_EXAMPLE1).unwrap(); + } + }); + } + + #[bench] + fn exp2_deserialize_perf_serde_json(b: &mut Bencher) { + b.iter(|| { + for _ in 0..LOOPS_NUM { + let _value: Value = serde_json::from_str(RFC7159_EXAMPLE2).unwrap(); + } + }); + } + + #[bench] + fn exp3_deserialize_perf_serde_json(b: &mut Bencher) { + b.iter(|| { + for _ in 0..LOOPS_NUM { + let _value: Value = serde_json::from_str(JSON_PARSE_TEST).unwrap(); + } + }); + } + + #[bench] + fn exp4_deserialize_perf_serde_json(b: &mut Bencher) { + b.iter(|| { + for _ in 0..LOOPS_NUM { + let _value: Value = serde_json::from_str(LONG_KEY_VALUE).unwrap(); + } + }); + } +} diff --git a/benches/serialize_json_cmp.rs b/benches/serialize_json_cmp.rs new file mode 100644 index 0000000000000000000000000000000000000000..27e0c3d21ecb804e2e6bb58904722d1c2c961c34 --- /dev/null +++ b/benches/serialize_json_cmp.rs @@ -0,0 +1,231 @@ +/* + * Copyright (c) 2023 Huawei Device Co., Ltd. + * Licensed 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. + */ + +//! Benchmarks for the json serialization + +#![feature(test)] + +mod task_helpers; + +#[cfg(test)] +mod deserialize_cmp { + extern crate test; + use crate::task_helpers::*; + use test::Bencher; + + use serde_json::Value; + use std::str::FromStr; + use ylong_json::JsonValue; + + #[bench] + fn null_serialize_perf_ylong_json(b: &mut Bencher) { + let value = JsonValue::from_str(NULL_EXAMPLE).unwrap(); + b.iter(|| { + for _ in 0..LOOPS_NUM { + let _ = value.to_compact_string(); + } + }); + } + + #[bench] + fn boolean_serialize_perf_ylong_json(b: &mut Bencher) { + let value = JsonValue::from_str(BOOLEAN_EXAMPLE).unwrap(); + b.iter(|| { + for _ in 0..LOOPS_NUM { + let _ = value.to_compact_string(); + } + }); + } + + #[bench] + fn number_serialize_perf_ylong_json(b: &mut Bencher) { + let value = JsonValue::from_str(NUMBER_EXAMPLE).unwrap(); + b.iter(|| { + for _ in 0..LOOPS_NUM { + let _ = value.to_compact_string(); + } + }); + } + + #[bench] + fn string_serialize_perf_ylong_json(b: &mut Bencher) { + let value = JsonValue::from_str(STRING_EXAMPLE).unwrap(); + b.iter(|| { + for _ in 0..LOOPS_NUM { + let _ = value.to_compact_string(); + } + }); + } + + #[bench] + fn array_serialize_perf_ylong_json(b: &mut Bencher) { + let value = JsonValue::from_str(ARRAY_EXAMPLE).unwrap(); + b.iter(|| { + for _ in 0..LOOPS_NUM { + let _ = value.to_compact_string(); + } + }); + } + + #[bench] + fn object_serialize_perf_ylong_json(b: &mut Bencher) { + let value = JsonValue::from_str(OBJECT_EXAMPLE).unwrap(); + b.iter(|| { + for _ in 0..LOOPS_NUM { + let _ = value.to_compact_string(); + } + }); + } + + #[bench] + fn exp1_serialize_perf_ylong_json(b: &mut Bencher) { + let value = JsonValue::from_str(RFC7159_EXAMPLE1).unwrap(); + b.iter(|| { + for _ in 0..LOOPS_NUM { + let _ = value.to_compact_string(); + } + }); + } + + #[bench] + fn exp2_serialize_perf_ylong_json(b: &mut Bencher) { + let value = JsonValue::from_str(RFC7159_EXAMPLE2).unwrap(); + b.iter(|| { + for _ in 0..LOOPS_NUM { + let _ = value.to_compact_string(); + } + }); + } + + #[bench] + fn exp3_serialize_perf_ylong_json(b: &mut Bencher) { + let value = JsonValue::from_str(JSON_PARSE_TEST).unwrap(); + b.iter(|| { + for _ in 0..LOOPS_NUM { + let _ = value.to_compact_string(); + } + }); + } + + #[bench] + fn exp4_serialize_perf_ylong_json(b: &mut Bencher) { + let value = JsonValue::from_str(LONG_KEY_VALUE).unwrap(); + b.iter(|| { + for _ in 0..LOOPS_NUM { + let _ = value.to_compact_string(); + } + }); + } + + #[bench] + fn null_serialize_perf_serde_json(b: &mut Bencher) { + let value: Value = serde_json::from_str(NULL_EXAMPLE).unwrap(); + b.iter(|| { + for _ in 0..LOOPS_NUM { + format!("{value}"); + } + }); + } + + #[bench] + fn boolean_serialize_perf_serde_json(b: &mut Bencher) { + let value: Value = serde_json::from_str(BOOLEAN_EXAMPLE).unwrap(); + b.iter(|| { + for _ in 0..LOOPS_NUM { + format!("{value}"); + } + }); + } + + #[bench] + fn number_serialize_perf_serde_json(b: &mut Bencher) { + let value: Value = serde_json::from_str(NUMBER_EXAMPLE).unwrap(); + b.iter(|| { + for _ in 0..LOOPS_NUM { + format!("{value}"); + } + }); + } + + #[bench] + fn string_serialize_perf_serde_json(b: &mut Bencher) { + let value: Value = serde_json::from_str(STRING_EXAMPLE).unwrap(); + b.iter(|| { + for _ in 0..LOOPS_NUM { + format!("{value}"); + } + }); + } + + #[bench] + fn array_serialize_perf_serde_json(b: &mut Bencher) { + let value: Value = serde_json::from_str(ARRAY_EXAMPLE).unwrap(); + b.iter(|| { + for _ in 0..LOOPS_NUM { + format!("{value}"); + } + }); + } + + #[bench] + fn object_serialize_perf_serde_json(b: &mut Bencher) { + let value: Value = serde_json::from_str(OBJECT_EXAMPLE).unwrap(); + b.iter(|| { + for _ in 0..LOOPS_NUM { + format!("{value}"); + } + }); + } + + #[bench] + fn exp1_serialize_perf_serde_json(b: &mut Bencher) { + let value: Value = serde_json::from_str(RFC7159_EXAMPLE1).unwrap(); + b.iter(|| { + for _ in 0..LOOPS_NUM { + format!("{value}"); + } + }); + } + + #[bench] + fn exp2_serialize_perf_serde_json(b: &mut Bencher) { + let value: Value = serde_json::from_str(RFC7159_EXAMPLE2).unwrap(); + b.iter(|| { + for _ in 0..LOOPS_NUM { + format!("{value}"); + } + }); + } + + #[bench] + fn exp3_serialize_perf_serde_json(b: &mut Bencher) { + let value: Value = serde_json::from_str(JSON_PARSE_TEST).unwrap(); + b.iter(|| { + for _ in 0..LOOPS_NUM { + format!("{value}"); + } + }); + } + + #[bench] + fn exp4_serialize_perf_serde_json(b: &mut Bencher) { + let value: Value = serde_json::from_str(LONG_KEY_VALUE).unwrap(); + b.iter(|| { + for _ in 0..LOOPS_NUM { + format!("{value}"); + } + }); + } +} diff --git a/benches/task_helpers/mod.rs b/benches/task_helpers/mod.rs new file mode 100644 index 0000000000000000000000000000000000000000..0d9775952c7766ca3d66eb3772d0295de9f9c219 --- /dev/null +++ b/benches/task_helpers/mod.rs @@ -0,0 +1,155 @@ +/* + * Copyright (c) 2023 Huawei Device Co., Ltd. + * Licensed 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. + */ + +pub const LOOPS_NUM: usize = 10; + +pub const NULL_EXAMPLE: &str = "null"; +pub const BOOLEAN_EXAMPLE: &str = "false"; +pub const NUMBER_EXAMPLE: &str = "12.34"; +pub const STRING_EXAMPLE: &str = "\"Hello\""; +pub const ARRAY_EXAMPLE: &str = "[false,null,12.34]"; +pub const OBJECT_EXAMPLE: &str = r#"{"key":"value"}"#; + +pub const RFC7159_EXAMPLE1: &str = r#" +{ + "Image": { + "Width": 800, + "Height": 600, + "Title": "View from 15th Floor", + "Thumbnail": { + "Url": "http://www.example.com/image/481989943", + "Height": 125, + "Width": 100 + }, + "Animated" : false, + "IDs": [116, 943, 234, 38793] + } +} +"#; + +pub const RFC7159_EXAMPLE2: &str = r#" +[ + { + "precision": "zip", + "Latitude": 37.7668, + "Longitude": -122.3959, + "Address": "", + "City": "SAN FRANCISCO", + "State": "CA", + "Zip": "94107", + "Country": "US" + }, + { + "precision": "zip", + "Latitude": 37.371991, + "Longitude": -122.026020, + "Address": "", + "City": "SUNNYVALE", + "State": "CA", + "Zip": "94085", + "Country": "US" + } +] +"#; + +pub const JSON_PARSE_TEST: &str = r#" +[ + { + "null1": null + }, + { + "boolean1": true, + "boolean2": false + }, + { + "number1": 0, + "number2": -0, + "number3": 123, + "number4": -123, + "number5": 123.456, + "number6": -123.456, + "number7": 123.456e+7, + "number8": 123.456e-7, + "number9": 123.456E+7, + "number10": 123.456E-7, + "number11": -123.456e+7, + "number12": -123.456e-7, + "number13": -123.456E+7, + "number14": -123.456E-7, + "number15": 0.0, + "number16": -0.0e+7, + "number17": 3e2 + }, + { + "string1": "", + "string2": "Hello World", + "string3": "abcdefghijklmnopqrstuvwxyz", + "string4": "ABCDEFGHIJKLMNOPQRSTUVWXYZ", + "string5": "0123456789", + "string6": " \b\f\n\r\t", + "string7": "\"\\\/", + "string8": "`1~!@#$%^&*()_+-={':[,]}|;.?", + "string9": "\u0123\u4567\u89AB\uCDEF\uabcd\uef4A" + }, + { + "array1": [], + "array2": [ + ], + "array3": [null,true,0.0,"string",[],{}], + "array4": [ + null , true, 0.0 , + "string", [] + , {} ], + "array5": [[[[[[["nest"]]]]]]] + }, + { + "object1": {}, + "object2": { + }, + "object3": {"key1":null,"key2":true,"key3":0.0,"key4":"string","key5":[],"key6":{}}, + "object4": { + "key1" : null , "key2" + : true , "key3" : + 0.0 , "key4":"string" , + "key5": [], "key6": { + } + }, + "object5": {"nest1": {"nest2": {"nest3": {"nest4": {}}}}} + }, + { + "": "key1", + "\/\\\"\uCAFE\uBABE\uAB98\uFCDE\ubcda\uef4A\b\f\n\r\t`1~!@#$%^&*()_+-=[]{}|;:',./<>?" : "key2" + }, + { + "key_value1" + : "value" + , "key_value2" : [ + ] , "key_value3" : + {} + } +] +"#; + +pub const LONG_KEY_VALUE: &str = r#" +{ + "long_key_value_object":{ + "-----LONG KEY-----uoTVt77ryiZ5GnfVXf6kEBJQS8hBMY2BMsyLyckIPrNEvknjp82jz9yatYV0S77uLb99nPR6WqSDPtrWzc1XHJVPLoIlxaDGKm4xB7KaFl95wdnYRvuyCEmrzdoZS1KtXyf31vYLD4r9BnFm6wBuefKvONcLNGi5bsZqq100MWmFXjQUYhd6nZDJWVTAtpF195PiyvoJiJxSkiwpallQCqTbcoZTMf5SJ7KH1umstVVPW6NvgRO5PwwHc2N7QytBvw": + "-----LONG VALUE-----by4iUNvpmeZ5ypvznYm7DSiY6gEgRy64yFGHB6pSgMGVRvElAnrSXpaSC8Exa9aMbx4hGkStSKMSbsk2t8JVxDqBKQVo7NdJiSwQf2p5YxFIU5aS2y4gazdDHcwuo7pqrp47AuXfxC799qUDD4q6VWD9u49Nuy7DXLjrdgLz17cC3uCaMwSZK3wc6Lu0Mri6Di4M9NEe36WGBN1xcmcHvm8GH7XXGikuuZ432HG76DEek1s99jHTzQZEILiDQAB", + + "-----LONG KEY-----by4iUNvpmeZ5ypvznYm7DSiY6gEgRy64yFGHB6pSgMGVRvElAnrSXpaSC8Exa9aMbx4hGkStSKMSbsk2t8JVxDqBKQVo7NdJiSwQf2p5YxFIU5aS2y4gazdDHcwuo7pqrp47AuXfxC799qUDD4q6VWD9u49Nuy7DXLjrdgLz17cC3uCaMwSZK3wc6Lu0Mri6Di4M9NEe36WGBN1xcmcHvm8GH7XXGikuuZ432HG76DEek1s99jHTzQZEILiDQAB": + "-----LONG VALUE-----uoTVt77ryiZ5GnfVXf6kEBJQS8hBMY2BMsyLyckIPrNEvknjp82jz9yatYV0S77uLb99nPR6WqSDPtrWzc1XHJVPLoIlxaDGKm4xB7KaFl95wdnYRvuyCEmrzdoZS1KtXyf31vYLD4r9BnFm6wBuefKvONcLNGi5bsZqq100MWmFXjQUYhd6nZDJWVTAtpF195PiyvoJiJxSkiwpallQCqTbcoZTMf5SJ7KH1umstVVPW6NvgRO5PwwHc2N7QytBvw" + } +} +"#; diff --git a/examples/ylong_json_example.rs b/examples/ylong_json_example.rs new file mode 100644 index 0000000000000000000000000000000000000000..1ff754fbe50f84cc1949b2ac632a8b9634fa4fb3 --- /dev/null +++ b/examples/ylong_json_example.rs @@ -0,0 +1,43 @@ +/* + * Copyright (c) 2023 Huawei Device Co., Ltd. + * Licensed 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. + */ + +//! cargo build --example ylong_json_example +//! Simple use examples of serialization and deserialization of JsonValue. + +use std::io::stdout; +use ylong_json::JsonValue; + +const JSON_TEXT: &str = r#" +{ + "null": null, + "true": true, + "false": false, + "number": 3.14, + "string": "Hello World!", + "array": [1, 2, 3], + "object": { + "key1": 1, + "key2": 2, + "key3": 3 + } +} +"#; + +fn main() { + let value = JsonValue::from_text(JSON_TEXT).unwrap(); + let mut console = stdout(); + value.formatted_encode(&mut console).unwrap(); + value.compact_encode(&mut console).unwrap(); +} diff --git a/examples/ylong_json_perf.rs b/examples/ylong_json_perf.rs new file mode 100644 index 0000000000000000000000000000000000000000..0472c3d17ea93bbe9b6dadba0f38066c1e23a5cc --- /dev/null +++ b/examples/ylong_json_perf.rs @@ -0,0 +1,75 @@ +/* + * Copyright (c) 2023 Huawei Device Co., Ltd. + * Licensed 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. + */ + +//! A performance testing suite for ylong_json. +//! +//! This performance testing suite compares the speed of the `ylong_json` crate +//! with `serde_json` for parsing JSON text and converting JSON objects into +//! strings. The test is run multiple times as defined by `LOOPS_NUM`. +//! +//! Example JSON used in this test represents an image object with various properties. + +use serde_json::Value; +use std::str::FromStr; +use std::time::Instant; +use ylong_json::JsonValue; + +const LOOPS_NUM: usize = 10000; +const JSON_TEXT: &str = r#" +{ + "Image": { + "Width": 800, + "Height": 600, + "Title": "View from 15th Floor", + "Thumbnail": { + "Url": "http://www.example.com/image/481989943", + "Height": 125, + "Width": 100 + }, + "Animated" : false, + "IDs": [116, 943, 234, 38793] + } +} +"#; + +fn main() { + let value = JsonValue::from_str(JSON_TEXT).unwrap(); + println!("{}", value.to_compact_string().unwrap()); + + let st = Instant::now(); + for _ in 0..LOOPS_NUM { + let value = JsonValue::from_str(JSON_TEXT).unwrap(); + let _ = value.to_compact_string(); + } + let ed = Instant::now(); + println!( + "ylong_json: {}ms", + ed.duration_since(st).as_secs_f64() * 1000f64 + ); + + let value: Value = serde_json::from_str(JSON_TEXT).unwrap(); + println!("{value}"); + + let st = Instant::now(); + for _ in 0..LOOPS_NUM { + let value: Value = serde_json::from_str(JSON_TEXT).unwrap(); + format!("{value}"); + } + let ed = Instant::now(); + println!( + "serde_json: {}ms", + ed.duration_since(st).as_secs_f64() * 1000f64 + ); +} diff --git a/src/adapter.rs b/src/adapter.rs new file mode 100644 index 0000000000000000000000000000000000000000..d9fdb8ffcbbbda133ea1f65a2d24b0d7c7d785a6 --- /dev/null +++ b/src/adapter.rs @@ -0,0 +1,3409 @@ +/* + * Copyright (c) 2023 Huawei Device Co., Ltd. + * Licensed 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. + */ + +#![allow(clippy::missing_safety_doc)] + +use crate::*; +use core::ptr::{null_mut, slice_from_raw_parts}; +use core::str::from_utf8_unchecked; +use libc::{c_char, c_double, c_int, c_longlong, c_void, strlen}; +use std::ffi::CString; + +/// Boolean value mapping. +const FALSE: c_int = 0; + +/// Operation success or failure. +const SUCCESS: c_int = 1; +const FAILURE: c_int = 0; + +/// Empty pointer of YlongJson* +const NULL_MUT_YLONG_JSON: *mut YlongJson = null_mut::(); +/// Empty pointer of char* +const NULL_MUT_CSTR: *mut c_char = null_mut::(); + +/// A void* pointer is passed to C for use. +pub type YlongJson = c_void; + +/// Parses a JSON text string. +/// Returns a JSON object on success and null on failure. +#[no_mangle] +pub unsafe extern "C" fn ylong_json_parse( + value: *mut c_char, + err_msg: *mut *mut c_char, +) -> *mut YlongJson { + // Using `ptr::slice_from_raw_parts` here dramatically + // reduces the cost of converting between char* and &[u8]. + let len = strlen(value); + let slice = &*slice_from_raw_parts(value as *mut u8, len); + + let value = match JsonValue::from_text(slice) { + Ok(v) => v, + Err(e) => { + // If an error occurs, writes error messages into err_msg. + *err_msg = CString::from_vec_unchecked(e.to_string().into_bytes()).into_raw(); + return NULL_MUT_YLONG_JSON; + } + }; + + Box::into_raw(Box::from(value)) as *mut YlongJson +} + +/// Frees a C string. +#[no_mangle] +pub unsafe extern "C" fn ylong_json_free_string(string: *mut c_char) { + if string.is_null() { + return; + } + + let _ = Box::from_raw(string); +} + +/// Outputs a JSON object to a string in plain format. +/// Returns a C string on success and null on failure. +#[no_mangle] +pub unsafe extern "C" fn ylong_json_print_unformatted(item: *const YlongJson) -> *mut c_char { + if item.is_null() { + return NULL_MUT_CSTR; + } + + let value = &mut *(item as *mut JsonValue); + + // Requests 256 bytes of memory in advance to improve output efficiency. + // Here we use `Vec::with_capacity(256)`, which performs better when `12 < string.len() < 256`. + // If here we use `Vec::new()`, it performs better when `string.len() < 32 || 256 > string.len()` + // In most cases, `12 < the average string.len() < 256`, so we use `Vec::with_capacity()`. + let mut vec = Vec::with_capacity(256); + if value.compact_encode(&mut vec).is_err() { + return NULL_MUT_CSTR; + } + + CString::from_vec_unchecked(vec).into_raw() +} + +/// Deletes a JSON object. +#[no_mangle] +pub unsafe extern "C" fn ylong_json_delete(item: *mut YlongJson) { + if item.is_null() { + return; + } + + let _ = Box::from_raw(item as *mut JsonValue); +} + +/// Duplicates a JSON object. +/// Return a new JSON object on success and null on failure. +#[no_mangle] +pub unsafe extern "C" fn ylong_json_duplicate( + item: *const YlongJson, + recurse: c_int, +) -> *mut YlongJson { + if item.is_null() { + return NULL_MUT_YLONG_JSON; + } + + if recurse == FALSE { + let value = &*(item as *mut JsonValue); + let value_clone = match value { + JsonValue::Array(_) => JsonValue::Array(Array::new()), + JsonValue::Object(_) => JsonValue::Object(Object::new()), + x => x.clone(), + }; + return Box::into_raw(Box::from(value_clone)) as *mut YlongJson; + } + + let value = &*(item as *mut JsonValue); + Box::into_raw(Box::from(value.clone())) as *mut YlongJson +} + +/// Creates a JSON null object and returns a new JSON null object. +#[no_mangle] +pub unsafe extern "C" fn ylong_json_create_null() -> *mut YlongJson { + Box::into_raw(Box::from(JsonValue::Null)) as *mut YlongJson +} + +/// Checks whether a JSON object is null. +/// Returns a boolean value indicating whether the object is null. +#[no_mangle] +pub unsafe extern "C" fn ylong_json_is_null(item: *mut YlongJson) -> c_int { + if item.is_null() { + return FALSE; + } + + let item = &*(item as *mut JsonValue); + + item.is_null() as c_int +} + +/// Creates a JSON boolean object. +#[no_mangle] +pub unsafe extern "C" fn ylong_json_create_bool(boolean: c_int) -> *mut YlongJson { + // If it is equal to 0, the result is false. Otherwise it is true. + Box::into_raw(Box::from(JsonValue::Boolean(boolean != FALSE))) as *mut YlongJson +} + +/// Checks whether a JSON object is a boolean. +#[no_mangle] +pub unsafe extern "C" fn ylong_json_is_bool(item: *const YlongJson) -> c_int { + if item.is_null() { + return FALSE; + } + + let item = &*(item as *mut JsonValue); + + item.is_boolean() as c_int +} + +/// Gets the boolean value of a JSON boolean object. +/// Returns a boolean value on success and an error code on failure. +#[no_mangle] +pub unsafe extern "C" fn ylong_json_get_value_from_bool( + boolean: *const YlongJson, + value: *mut c_int, +) -> c_int { + if boolean.is_null() { + return FAILURE; + } + + let boolean = &*(boolean as *mut JsonValue); + let boolean = match boolean.try_as_boolean() { + Ok(b) => b, + Err(_) => return FAILURE, + }; + // For c_int value, true maps to 1, while false maps to 0. + *value = *boolean as c_int; + SUCCESS +} + +/// Sets the boolean value of a JSON boolean object. +/// Returns a `c_int` indicating whether the operation was successful (SUCCESS) or failed (FAILURE). +#[no_mangle] +pub unsafe extern "C" fn ylong_json_set_value_to_bool( + boolean: *mut YlongJson, + value: c_int, +) -> c_int { + if boolean.is_null() { + return FAILURE; + } + + let boolean = &mut *(boolean as *mut JsonValue); + let boolean = match boolean.try_as_mut_boolean() { + Ok(b) => b, + Err(_) => return FAILURE, + }; + // The *boolean is false if value is 0, and true if value is not 1. + *boolean = value != FALSE; + SUCCESS +} + +/// Creates a JSON double number object. +/// Returns a pointer to the newly created JSON number. +#[no_mangle] +pub unsafe extern "C" fn ylong_json_create_double_number(number: c_double) -> *mut YlongJson { + Box::into_raw(Box::from(JsonValue::Number(Number::Float(number)))) as *mut YlongJson +} + +/// Creates a JSON integer number object. +/// Returns a pointer to the newly created JSON number. +#[no_mangle] +pub unsafe extern "C" fn ylong_json_create_int_number(number: c_longlong) -> *mut YlongJson { + Box::into_raw(Box::from(JsonValue::Number(Number::Signed(number)))) as *mut YlongJson +} + +/// Checks whether a JSON object is a number. +/// Returns a `c_int` where TRUE indicates that the item is a number, and FALSE indicates otherwise. +#[no_mangle] +pub unsafe extern "C" fn ylong_json_is_number(item: *const YlongJson) -> c_int { + if item.is_null() { + return FALSE; + } + + let item = &*(item as *mut JsonValue); + item.is_number() as c_int +} + +/// Checks whether a JSON object is a double number. +/// Returns a `c_int` where TRUE indicates that the number is a double, and FALSE indicates otherwise. +#[no_mangle] +pub unsafe extern "C" fn ylong_json_is_double_number(item: *const YlongJson) -> c_int { + if item.is_null() { + return FALSE; + } + + let item = &*(item as *mut JsonValue); + match item.try_as_number() { + Ok(n) => matches!(n, Number::Float(_)) as c_int, + Err(_) => FALSE, + } +} + +/// Checks whether a JSON object is an integer number. +/// Returns a `c_int` where TRUE indicates that the number is an integer, and FALSE indicates otherwise. +#[no_mangle] +pub unsafe extern "C" fn ylong_json_is_int_number(item: *const YlongJson) -> c_int { + if item.is_null() { + return FALSE; + } + + let item = &*(item as *mut JsonValue); + match item.try_as_number() { + Ok(n) => matches!(n, Number::Signed(_) | Number::Unsigned(_)) as c_int, + Err(_) => FALSE, + } +} + +/// Gets the double value of a JSON number object. +/// Returns a `c_int` indicating whether the operation was successful (SUCCESS) or failed (FAILURE). +#[no_mangle] +pub unsafe extern "C" fn ylong_json_get_double_value_from_number( + number: *const YlongJson, + value: *mut c_double, +) -> c_int { + if number.is_null() { + return FAILURE; + } + + let number = &*(number as *mut JsonValue); + let number = match number.try_as_number() { + Ok(n) => n, + Err(_) => return FAILURE, + }; + // Coercing u64 or i64 to f64 may result in a loss of data accuracy. + match number { + Number::Float(f) => *value = *f as c_double, + Number::Unsigned(u) => *value = *u as c_double, + Number::Signed(i) => *value = *i as c_double, + } + SUCCESS +} + +/// Gets the integer value of a JSON number object. +/// Returns a `c_int` indicating whether the operation was successful (SUCCESS) or failed (FAILURE). +#[no_mangle] +pub unsafe extern "C" fn ylong_json_get_int_value_from_number( + number: *const YlongJson, + value: *mut c_longlong, +) -> c_int { + if number.is_null() { + return FAILURE; + } + + let number = &*(number as *mut JsonValue); + let number = match number.try_as_number() { + Ok(n) => n, + Err(_) => return FAILURE, + }; + // Coercing u64 or i64 or f64 to i64 may result in a loss of data accuracy. + match number { + Number::Float(f) => *value = *f as c_longlong, + Number::Unsigned(u) => *value = *u as c_longlong, + Number::Signed(i) => *value = *i as c_longlong, + } + SUCCESS +} + +/// Sets the double value of a JSON number object. +/// Returns a `c_int` indicating whether the operation was successful (SUCCESS) or failed (FAILURE). +#[no_mangle] +pub unsafe extern "C" fn ylong_json_set_double_value_to_number( + number: *mut YlongJson, + value: c_double, +) -> c_int { + if number.is_null() { + return FAILURE; + } + + let number = &mut *(number as *mut JsonValue); + let number = match number.try_as_mut_number() { + Ok(n) => n, + Err(_) => return FAILURE, + }; + *number = Number::Float(value); + SUCCESS +} + +/// Sets the integer value of a JSON number object. +/// Returns a `c_int` indicating whether the operation was successful (SUCCESS) or failed (FAILURE). +#[no_mangle] +pub unsafe extern "C" fn ylong_json_set_int_value_to_number( + number: *mut YlongJson, + value: c_longlong, +) -> c_int { + if number.is_null() { + return FAILURE; + } + + let number = &mut *(number as *mut JsonValue); + let number = match number.try_as_mut_number() { + Ok(n) => n, + Err(_) => return FAILURE, + }; + *number = Number::Signed(value); + SUCCESS +} + +/// Creates a `YlongJson` string from a given C-style string. +/// If the input string is null, it returns a null `YlongJson`. +#[no_mangle] +pub unsafe extern "C" fn ylong_json_create_string(string: *const c_char) -> *mut YlongJson { + if string.is_null() { + return NULL_MUT_YLONG_JSON; + } + + let len = strlen(string); + let slice = &*slice_from_raw_parts(string as *mut u8, len); + let string = CString::from_vec_unchecked(slice.to_vec()); + Box::into_raw(Box::from(JsonValue::String(string))) as *mut YlongJson +} + +/// Checks if the `YlongJson` item is a string. +/// Returns `FALSE` if the item is null or not a string, and `TRUE` otherwise. +#[no_mangle] +pub unsafe extern "C" fn ylong_json_is_string(item: *const YlongJson) -> c_int { + if item.is_null() { + return FALSE; + } + + let item = &*(item as *mut JsonValue); + item.is_string() as c_int +} + +/// The char* returned by this function differs from the original data, +/// meaning that any changes to this char* will not be reflected in the original data. +#[no_mangle] +pub unsafe extern "C" fn ylong_json_get_value_from_string( + string: *const YlongJson, + value: *mut *mut c_char, +) -> c_int { + if string.is_null() { + return FAILURE; + } + + let string = &*(string as *mut JsonValue); + let string = match string.try_as_string() { + Ok(s) => s, + Err(_) => return FAILURE, + }; + // If `c_adapter` feature is on, the pointer of the inner char arrays can be obtained directly, + // because the string pointer actually points to a `CString` + *value = string.as_ptr() as *mut c_char; + SUCCESS +} + +/// Sets a `YlongJson` string to a given C-style string. +/// If the `YlongJson` string or the input string is null, it returns `FAILURE`. +#[no_mangle] +pub unsafe extern "C" fn ylong_json_set_value_to_string( + string: *mut YlongJson, + value: *const c_char, +) -> c_int { + if string.is_null() || value.is_null() { + return FAILURE; + } + + let string = &mut *(string as *mut JsonValue); + let string = match string.try_as_mut_string() { + Ok(s) => s, + Err(_) => return FAILURE, + }; + let len = strlen(value); + let slice = &*slice_from_raw_parts(value as *mut u8, len); + *string = CString::from_vec_unchecked(slice.to_vec()); + SUCCESS +} + +/// Creates a `YlongJson` array. +#[no_mangle] +pub unsafe extern "C" fn ylong_json_create_array() -> *mut YlongJson { + Box::into_raw(Box::from(JsonValue::Array(Array::new()))) as *mut YlongJson +} + +/// Checks if the `YlongJson` item is an array. +/// Returns `FALSE` if the item is null or not an array, and `TRUE` otherwise. +#[no_mangle] +pub unsafe extern "C" fn ylong_json_is_array(item: *const YlongJson) -> c_int { + if item.is_null() { + return FALSE; + } + + let item = &*(item as *mut JsonValue); + item.is_array() as c_int +} + +/// Gets the size of a `YlongJson` array. +/// If the `YlongJson` array or the size is null, it returns `FAILURE`. +#[no_mangle] +pub unsafe extern "C" fn ylong_json_get_array_size( + array: *const YlongJson, + size: *mut c_int, +) -> c_int { + if array.is_null() || size.is_null() { + return FAILURE; + } + + let array = &*(array as *mut JsonValue); + let array = match array.try_as_array() { + Ok(a) => a, + Err(_) => return FAILURE, + }; + + *size = array.len() as c_int; + SUCCESS +} + +/// Gets a `YlongJson` item from an array by index. +/// Returns null `YlongJson` if the array is null, the item doesn't exist, or any error occurs. +#[no_mangle] +pub unsafe extern "C" fn ylong_json_get_array_item( + array: *const YlongJson, + index: c_int, +) -> *mut YlongJson { + if array.is_null() { + return NULL_MUT_YLONG_JSON; + } + + let array_ref = &mut *(array as *mut JsonValue); + let array_ref = match array_ref.try_as_mut_array() { + Ok(a) => a, + Err(_) => return NULL_MUT_YLONG_JSON, + }; + + if index as usize >= array_ref.len() { + return NULL_MUT_YLONG_JSON; + } + + array_ref.get_mut(index as usize).unwrap() as *mut JsonValue as *mut YlongJson +} + +/// Adds a `YlongJson` item to an array. +/// Returns `FAILURE` if the array or the item is null, and `SUCCESS` otherwise. +#[no_mangle] +pub unsafe extern "C" fn ylong_json_add_item_to_array( + array: *mut YlongJson, + item: *mut YlongJson, +) -> c_int { + if array.is_null() || item.is_null() { + return FAILURE; + } + + let array_ref = &mut *(array as *mut JsonValue); + let array_ref = match array_ref.try_as_mut_array() { + Ok(a) => a, + Err(_) => return FAILURE, + }; + + let value = Box::from_raw(item as *mut JsonValue); + array_ref.push(*value); + + SUCCESS +} + +/// Replaces a `YlongJson` item in an array by index with a new item. +/// Returns `FAILURE` if the array or the new item is null, the index is out of bounds, or any error occurs, and `SUCCESS` otherwise. +#[no_mangle] +pub unsafe extern "C" fn ylong_json_replace_array_item_by_index( + array: *mut YlongJson, + index: c_int, + new_item: *mut YlongJson, +) -> c_int { + if array.is_null() || new_item.is_null() { + return FAILURE; + } + + let array_ref = &mut *(array as *mut JsonValue); + let array_ref = match array_ref.try_as_mut_array() { + Ok(o) => o, + Err(_) => return FAILURE, + }; + + if let Some(value) = array_ref.get_mut(index as usize) { + let new_value = Box::from_raw(new_item as *mut JsonValue); + + *value = *new_value; + + return SUCCESS; + } + FAILURE +} + +/// Removes a `YlongJson` item from an array by index. +/// Returns null `YlongJson` if the array is null, the item doesn't exist, or any error occurs. +#[no_mangle] +pub unsafe extern "C" fn ylong_json_remove_array_item_by_index( + array: *mut YlongJson, + index: c_int, +) -> *mut YlongJson { + if array.is_null() { + return NULL_MUT_YLONG_JSON; + } + + let array = &mut *(array as *mut JsonValue); + let array = match array.try_as_mut_array() { + Ok(a) => a, + Err(_) => return NULL_MUT_YLONG_JSON, + }; + + // Uses method 'remove' of Array, but not the method 'remove' of underlying data structure. + if let Some(v) = Array::remove(array, index as usize) { + return Box::into_raw(Box::new(v)) as *mut YlongJson; + } + NULL_MUT_YLONG_JSON +} + +/// Deletes a `YlongJson` item from an array by index. +#[no_mangle] +pub unsafe extern "C" fn ylong_json_delete_array_item_by_index( + array: *mut YlongJson, + index: c_int, +) { + if array.is_null() { + return; + } + + let array = &mut *(array as *mut JsonValue); + let array = match array.try_as_mut_array() { + Ok(a) => a, + Err(_) => return, + }; + array.remove(index as usize); +} + +/// In list_array mode, it is more efficient to get a node through this method and then delete it. +#[cfg(feature = "list_array")] +#[no_mangle] +pub unsafe extern "C" fn ylong_json_get_array_node( + array: *mut YlongJson, + index: c_int, +) -> *mut YlongJson { + if array.is_null() { + return NULL_MUT_YLONG_JSON; + } + + let array_ref = &mut *(array as *mut JsonValue); + let array_ref = match array_ref.try_as_mut_array() { + Ok(a) => a, + Err(_) => return NULL_MUT_YLONG_JSON, + }; + + if index as usize >= array_ref.len() { + return NULL_MUT_YLONG_JSON; + } + + let node = array_ref.get_node_mut(index as usize).unwrap(); + node as *mut Node as *mut YlongJson +} + +/// Retrieves a `YlongJson` item from an array node. +#[cfg(feature = "list_array")] +#[no_mangle] +pub unsafe extern "C" fn ylong_json_get_item_from_array_node( + array_node: *mut YlongJson, +) -> *mut YlongJson { + if array_node.is_null() { + return NULL_MUT_YLONG_JSON; + } + let node = &mut *(array_node as *mut Node); + node.get_element_mut() as *mut JsonValue as *mut YlongJson +} + +/// Adds a `YlongJson` item to an array, then returns the node. +/// Returns null `YlongJson` if the array or the item is null. +#[cfg(feature = "list_array")] +#[no_mangle] +pub unsafe extern "C" fn ylong_json_add_item_to_array_then_get_node( + array: *mut YlongJson, + item: *mut YlongJson, +) -> *mut YlongJson { + if array.is_null() || item.is_null() { + return NULL_MUT_YLONG_JSON; + } + + let array_ref = &mut *(array as *mut JsonValue); + let array_ref = match array_ref.try_as_mut_array() { + Ok(a) => a, + Err(_) => return NULL_MUT_YLONG_JSON, + }; + + let value = Box::from_raw(item as *mut JsonValue); + array_ref.push(*value); + + array_ref.last_node_mut().unwrap() as *mut Node as *mut YlongJson +} + +/// Replaces an item of an array node with a new item. +/// Returns `FAILURE` if the array node or the new item is null, and `SUCCESS` otherwise. +#[cfg(feature = "list_array")] +#[no_mangle] +pub unsafe extern "C" fn ylong_json_replace_item_of_array_node( + array_node: *mut YlongJson, + new_item: *mut YlongJson, +) -> c_int { + if array_node.is_null() || new_item.is_null() { + return FAILURE; + } + + let node = &mut *(array_node as *mut Node); + let value = node.get_element_mut(); + + let new_value = Box::from_raw(new_item as *mut JsonValue); + *value = *new_value; + SUCCESS +} + +/// Removes an array node. +#[cfg(feature = "list_array")] +#[no_mangle] +pub unsafe extern "C" fn ylong_json_remove_array_node( + array_node: *mut YlongJson, +) -> *mut YlongJson { + if array_node.is_null() { + return NULL_MUT_YLONG_JSON; + } + + let node = &mut *(array_node as *mut Node); + Box::into_raw(Box::new(node.remove_self().unwrap())) as *mut YlongJson +} + +/// Deletes an array node. +#[cfg(feature = "list_array")] +#[no_mangle] +pub unsafe extern "C" fn ylong_json_delete_array_node(array_node: *mut YlongJson) { + if array_node.is_null() { + return; + } + + let node = &mut *(array_node as *mut Node); + let _ = node.remove_self(); +} + +/// Creates a `YlongJson` object. +#[no_mangle] +pub unsafe extern "C" fn ylong_json_create_object() -> *mut YlongJson { + Box::into_raw(Box::from(JsonValue::Object(Object::new()))) as *mut YlongJson +} + +/// Checks if the `YlongJson` item is an object. +/// Returns `FALSE` if the item is null or not an object, and `TRUE` otherwise. +#[no_mangle] +pub unsafe extern "C" fn ylong_json_is_object(item: *const YlongJson) -> c_int { + if item.is_null() { + return FALSE; + } + + let item = &*(item as *mut JsonValue); + item.is_object() as c_int +} + +/// Gets the size of a `YlongJson` object. +/// If the `YlongJson` object or the size is null, it returns `FAILURE`. +#[no_mangle] +pub unsafe extern "C" fn ylong_json_get_object_size( + object: *mut YlongJson, + size: *mut c_int, +) -> c_int { + if object.is_null() || size.is_null() { + return FAILURE; + } + + let object = &mut *(object as *mut JsonValue); + let object = match object.try_as_mut_object() { + Ok(o) => o, + Err(_) => return FAILURE, + }; + + *size = object.len() as c_int; + SUCCESS +} + +/// Checks if a JSON object has a specific item. +/// Returns a `c_int` indicating whether the item exists (TRUE) or not (FALSE). +#[no_mangle] +pub unsafe extern "C" fn ylong_json_has_object_item( + object: *mut YlongJson, + string: *const c_char, +) -> c_int { + if object.is_null() || string.is_null() { + return FALSE; + } + + let object = &*(object as *mut JsonValue); + let object = match object.try_as_object() { + Ok(o) => o, + Err(_) => return FALSE, + }; + + // Using `ptr::slice_from_raw_parts` here dramatically + // reduces the cost of converting between char* and &[u8]. + // Then use `from_utf8_unchecked` to further reduce cost. + let len = strlen(string); + let slice = &*slice_from_raw_parts(string as *mut u8, len); + let str = from_utf8_unchecked(slice); + + object.contains_key(str) as c_int +} + +/// Retrieves an item from a JSON object by key. +/// Returns a mutable pointer to the retrieved JSON item. +#[no_mangle] +pub unsafe extern "C" fn ylong_json_get_object_item( + object: *const YlongJson, + string: *const c_char, +) -> *mut YlongJson { + // If object is empty, the search fails. + if object.is_null() || string.is_null() { + return NULL_MUT_YLONG_JSON; + } + + let object_ref = &mut *(object as *mut JsonValue); + + // If the type is not object, return err. + let object_ref = match object_ref.try_as_mut_object() { + Ok(o) => o, + Err(_) => return NULL_MUT_YLONG_JSON, + }; + + // Using `ptr::slice_from_raw_parts` here dramatically + // reduces the cost of converting between char* and &[u8]. + // Then use `from_utf8_unchecked` to further reduce cost. + let len = strlen(string); + let slice = &*slice_from_raw_parts(string as *mut u8, len); + let index = from_utf8_unchecked(slice); + + let target = match object_ref.get_mut(index) { + Some(v) => v, + None => return NULL_MUT_YLONG_JSON, + }; + target as *mut JsonValue as *mut YlongJson +} + +/// Adds an item to a JSON object. +/// Returns a `c_int` indicating whether the operation was successful (SUCCESS) or failed (FAILURE). +#[no_mangle] +pub unsafe extern "C" fn ylong_json_add_item_to_object( + object: *mut YlongJson, + string: *const c_char, + item: *mut YlongJson, +) -> c_int { + // If object or string or item is empty, returns FAILED. + if object.is_null() || string.is_null() || item.is_null() { + return FAILURE; + } + + let object_ref = &mut *(object as *mut JsonValue); + let object_ref = match object_ref.try_as_mut_object() { + Ok(o) => o, + Err(_) => return FAILURE, + }; + + // Using `ptr::slice_from_raw_parts` here dramatically + // reduces the cost of converting between char* and &[u8]. + // Then use `from_utf8_unchecked` to further reduce cost. + let len = strlen(string); + let slice = &*slice_from_raw_parts(string as *mut u8, len); + let index = from_utf8_unchecked(slice); + + let value = Box::from_raw(item as *mut JsonValue); + + object_ref.insert(String::from(index), *value); + + SUCCESS +} + +/// Replaces an item in a JSON object by key. +/// Returns a `c_int` indicating whether the operation was successful (SUCCESS) or failed (FAILURE). +#[no_mangle] +pub unsafe extern "C" fn ylong_json_replace_object_item_by_index( + object: *mut YlongJson, + index: *const c_char, + new_item: *mut YlongJson, +) -> c_int { + if object.is_null() || index.is_null() || new_item.is_null() { + return FAILURE; + } + + let object_ref = &mut *(object as *mut JsonValue); + let object_ref = match object_ref.try_as_mut_object() { + Ok(o) => o, + Err(_) => return FAILURE, + }; + + // Using `ptr::slice_from_raw_parts` here dramatically + // reduces the cost of converting between char* and &[u8]. + // Then use `from_utf8_unchecked` to further reduce cost. + let len = strlen(index); + let slice = &*slice_from_raw_parts(index as *mut u8, len); + let index = from_utf8_unchecked(slice); + + if let Some(value) = object_ref.get_mut(index) { + let new_value = Box::from_raw(new_item as *mut JsonValue); + + *value = *new_value; + + return SUCCESS; + } + + FAILURE +} + +/// Removes an item in a JSON object by index. +/// Returns a new JSON object without the item if successful, NULL_MUT_YLONG_JSON otherwise. +#[no_mangle] +pub unsafe extern "C" fn ylong_json_remove_object_item_by_index( + object: *mut YlongJson, + index: *const c_char, +) -> *mut YlongJson { + if object.is_null() || index.is_null() { + return NULL_MUT_YLONG_JSON; + } + + let object = &mut *(object as *mut JsonValue); + + // Using `ptr::slice_from_raw_parts` here dramatically + // reduces the cost of converting between char* and &[u8]. + // Then use `from_utf8_unchecked` to further reduce cost. + let len = strlen(index); + let slice = &*slice_from_raw_parts(index as *mut u8, len); + let index = from_utf8_unchecked(slice); + + if let Some(v) = object.remove(index) { + return Box::into_raw(Box::new(v)) as *mut YlongJson; + } + NULL_MUT_YLONG_JSON +} + +/// Deletes an item in a JSON object by index. +/// Does not return a value. +#[no_mangle] +pub unsafe extern "C" fn ylong_json_delete_object_item_by_index( + object: *mut YlongJson, + index: *const c_char, +) { + if object.is_null() || index.is_null() { + return; + } + + let object = &mut *(object as *mut JsonValue); + + // Using ptr::slice_from_raw_parts here dramatically + // reduces the cost of converting between char* and &[u8]. + // Then use from_utf8_unchecked to further reduce cost. + let len = strlen(index); + let slice = &*slice_from_raw_parts(index as *mut u8, len); + let index = from_utf8_unchecked(slice); + + object.remove(index); +} + +/// Gets all items from a JSON object. +/// Returns SUCCESS if the operation is successful, FAILURE otherwise. +#[no_mangle] +pub unsafe extern "C" fn ylong_json_get_all_object_items( + object: *mut YlongJson, + key: *mut *mut c_char, + value: *mut *mut YlongJson, + len: *mut c_int, +) -> c_int { + if object.is_null() || key.is_null() || value.is_null() || len.is_null() { + return FAILURE; + } + + let object = &mut *(object as *mut JsonValue); + let object = match object.try_as_mut_object() { + Ok(o) => o, + Err(_) => return FAILURE, + }; + + for (n, (k, v)) in object.iter_mut().enumerate() { + // k.clone ().into_bytes() is more efficient than k.as_bytes ().to_vec(). + let k = CString::from_vec_unchecked(k.clone().into_bytes()).into_raw(); + let v = v as *mut JsonValue as *mut YlongJson; + *(key.add(n)) = k; + *(value.add(n)) = v; + } + *len = object.len() as c_int; + SUCCESS +} + +/// Applies a function to each item in a JSON object. +/// Returns SUCCESS if the operation is successful, FAILURE otherwise. +#[no_mangle] +pub unsafe extern "C" fn ylong_json_for_each_object_item( + object: *mut YlongJson, + func: unsafe extern "C" fn(*mut YlongJson), +) -> c_int { + if object.is_null() { + return FAILURE; + } + + let object = &mut *(object as *mut JsonValue); + let object = match object.try_as_mut_object() { + Ok(o) => o, + Err(_) => return FAILURE, + }; + + object.iter_mut().for_each(|(_k, v)| { + let value = v as *mut JsonValue as *mut YlongJson; + func(value); + }); + SUCCESS +} + +/// Gets an object node from a JSON object by key. +/// Returns a pointer to the object node if successful, NULL_MUT_YLONG_JSON otherwise. +#[cfg(feature = "list_object")] +#[no_mangle] +pub unsafe extern "C" fn ylong_json_get_object_node( + object: *const YlongJson, + string: *const c_char, +) -> *mut YlongJson { + // If object is empty, the search fails. + if object.is_null() || string.is_null() { + return NULL_MUT_YLONG_JSON; + } + + let object_ref = &mut *(object as *mut JsonValue); + + // If the type is not object, returns err. + let object_ref = match object_ref.try_as_mut_object() { + Ok(o) => o, + Err(_) => return NULL_MUT_YLONG_JSON, + }; + + // Using `ptr::slice_from_raw_parts` here dramatically + // reduces the cost of converting between char* and &[u8]. + // Then use `from_utf8_unchecked` to further reduce cost. + let len = strlen(string); + let slice = &*slice_from_raw_parts(string as *mut u8, len); + let index = from_utf8_unchecked(slice); + + // When using list to get a node, the return value points to the memory is CursorMut. + let target = match object_ref.get_node_mut(index) { + Some(v) => v, + None => return NULL_MUT_YLONG_JSON, + }; + target as *mut Node<(String, JsonValue)> as *mut YlongJson +} + +/// Gets an item from an object node. +/// Returns a pointer to the item if successful, NULL_MUT_YLONG_JSON otherwise. +#[cfg(feature = "list_object")] +#[no_mangle] +pub unsafe extern "C" fn ylong_json_get_item_from_object_node( + object_node: *mut YlongJson, +) -> *mut YlongJson { + if object_node.is_null() { + return NULL_MUT_YLONG_JSON; + } + + let node = &mut *(object_node as *mut Node<(String, JsonValue)>); + (&mut node.get_element_mut().1) as *mut JsonValue as *mut YlongJson +} + +/// Adds an item to a JSON object, then returns a pointer to the object node. +/// Returns a pointer to the object node if successful, NULL_MUT_YLONG_JSON otherwise. +#[cfg(feature = "list_object")] +#[no_mangle] +pub unsafe extern "C" fn ylong_json_add_item_to_object_then_get_node( + object: *mut YlongJson, + string: *const c_char, + item: *mut YlongJson, +) -> *mut YlongJson { + // If object or item is empty, returns NULL_MUT_YLONG_JSON. + if object.is_null() || string.is_null() || item.is_null() { + return NULL_MUT_YLONG_JSON; + } + + let object_ref = &mut *(object as *mut JsonValue); + let object_ref = match object_ref.try_as_mut_object() { + Ok(v) => v, + Err(_) => return NULL_MUT_YLONG_JSON, + }; + + // Using `ptr::slice_from_raw_parts` here dramatically + // reduces the cost of converting between char* and &[u8]. + // Then use `from_utf8_unchecked` to further reduce cost. + let len = strlen(string); + let slice = &*slice_from_raw_parts(string as *mut u8, len); + let string = from_utf8_unchecked(slice); + + let value = Box::from_raw(item as *mut JsonValue); + object_ref.insert(String::from(string), *value); + + let target = object_ref.last_node_mut().unwrap(); + target as *mut Node<(String, JsonValue)> as *mut YlongJson +} + +/// Replaces an item in an object node. +/// Returns SUCCESS if the operation is successful, FAILURE otherwise. +#[cfg(feature = "list_object")] +#[no_mangle] +pub unsafe extern "C" fn ylong_json_replace_item_of_object_node( + object_node: *mut YlongJson, + new_item: *mut YlongJson, +) -> c_int { + if object_node.is_null() || new_item.is_null() { + return FAILURE; + } + + let node = &mut *(object_node as *mut Node<(String, JsonValue)>); + let (_, value) = node.get_element_mut(); + let new_value = Box::from_raw(new_item as *mut JsonValue); + *value = *new_value; + + SUCCESS +} + +/// Removes an object node. +/// Returns a pointer to the removed item if successful, NULL_MUT_YLONG_JSON otherwise. +#[cfg(feature = "list_object")] +#[no_mangle] +pub unsafe extern "C" fn ylong_json_remove_object_node( + object_node: *mut YlongJson, +) -> *mut YlongJson { + if object_node.is_null() { + return NULL_MUT_YLONG_JSON; + } + + let node = &mut *(object_node as *mut Node<(String, JsonValue)>); + Box::into_raw(Box::new(node.remove_self().unwrap().1)) as *mut YlongJson +} + +/// Deletes a node from a JSON object. +#[cfg(feature = "list_object")] +#[no_mangle] +pub unsafe extern "C" fn ylong_json_delete_object_node(object_node: *mut YlongJson) { + if object_node.is_null() { + return; + } + + let node = &mut *(object_node as *mut Node<(String, JsonValue)>); + let _ = node.remove_self(); +} + +#[cfg(test)] +mod ut_adapter { + use crate::*; + use libc::*; + use std::ffi::{CStr, CString}; + use std::mem::size_of; + use std::ptr::{null, null_mut}; + + const JSON_TEXT: &str = r#" +{ + "null": null, + "true": true, + "false": false, + "number": 3.14, + "string": "Hello World!", + "array": [1, 2, 3], + "object": { + "key1": 1, + "key2": 2, + "key3": 3 + } +} +"#; + + unsafe fn str_to_c_char(str: &str) -> *mut c_char { + CString::from_vec_unchecked(str.as_bytes().to_vec()).into_raw() + } + + /// UT test for `ylong_json_parse`. + /// + /// # Title + /// ut_ylong_json_parse + /// + /// # Brief + /// 1. Calls `ylong_json_parse` to generate a JsonValue as YlongJson*. + /// 2. Checks if the test results are correct. + #[test] + fn ut_ylong_json_parse() { + unsafe { + // Passes in the correct syntax text string. + let str = str_to_c_char(JSON_TEXT); + let err = null_mut::(); + let json = ylong_json_parse(str, &err as *const *mut c_char as *mut *mut c_char); + // No error message. + assert!(err.is_null()); + // The data structure is correct. + assert!(!json.is_null()); + + // Destruction + let _ = Box::from_raw(str); + ylong_json_delete(json); + + // Passes in the incorrect syntax text string. + let str = str_to_c_char("{"); + let err = null_mut::(); + let json = ylong_json_parse(str, &err as *const *mut c_char as *mut *mut c_char); + // Here is an error message. + assert!(!err.is_null()); + // No correct syntax structure. + assert!(json.is_null()); + + // Destruction + ylong_json_free_string(err); + let _ = Box::from_raw(str); + ylong_json_delete(json); + } + } + + //noinspection SpellCheckingInspection + //noinspection ALL + /// UT test for `ylong_json_free_string`. + /// + /// # Title + /// ut_ylong_json_free_string + /// + /// # Brief + /// 1. Calls `ylong_json_free_string` to free a YlongJson*(`C` string). + /// 2. Checks if the test results are correct. + #[test] + fn ut_ylong_json_free_string() { + unsafe { + // Null ptr scene, if the process does not exit abnormally, it is successful. + let string = null_mut(); + ylong_json_free_string(string); + + let str = str_to_c_char(JSON_TEXT); + let err = null_mut::(); + let json = ylong_json_parse(str, &err as *const *mut c_char as *mut *mut c_char); + assert!(err.is_null()); + assert!(!json.is_null()); + + // The char* generated by `ylong_json_print_unformatted` needs + // to be destructed by calling `ylong_json_free_string`. + let result = ylong_json_print_unformatted(json); + ylong_json_free_string(result); + + // Destruction + let _ = Box::from_raw(str); + ylong_json_delete(json); + } + } + + /// UT test for `ylong_json_print_unformatted`. + /// + /// # Title + /// ut_ylong_json_print_unformatted + /// + /// # Brief + /// 1. Calls `ylong_json_print_unformatted` to print the value as `C` string. + /// 2. Checks if the test results are correct. + #[test] + fn ut_ylong_json_print_unformatted() { + unsafe { + // Null ptr + let json = null_mut(); + assert!(ylong_json_print_unformatted(json).is_null()); + + // Correct scene + let str = str_to_c_char("{\"array\":[1,2,3]}"); + let err = null_mut::(); + let json = ylong_json_parse(str, &err as *const *mut c_char as *mut *mut c_char); + assert!(err.is_null()); + assert!(!json.is_null()); + + let result = ylong_json_print_unformatted(json); + let result = CString::from_raw(result).into_string().unwrap(); + assert_eq!(result, "{\"array\":[1,2,3]}"); + + // Destruction + let _ = Box::from_raw(str); + ylong_json_delete(json); + } + } + + /// UT test for `ylong_json_delete`. + /// + /// # Title + /// ut_ylong_json_delete + /// + /// # Brief + /// 1. Calls `ylong_json_delete` to delete the value. + /// 2. Checks if the test results are correct. + #[test] + fn ut_ylong_json_delete() { + unsafe { + // Null ptr scene, if the process does not exit abnormally, it is successful. + let json = null_mut(); + ylong_json_delete(json); + + // The YlongJson* generated by `ylong_json_parse` needs + // to be destructed by calling `ylong_json_delete`. + let str = str_to_c_char(JSON_TEXT); + let err = null_mut::(); + let json = ylong_json_parse(str, &err as *const *mut c_char as *mut *mut c_char); + assert!(err.is_null()); + assert!(!json.is_null()); + let _ = Box::from_raw(str); + ylong_json_delete(json); + + // If the YlongJson* generated by the function starting with + // `ylong_json_create` is not inserted into another YlongJson*, + // YlongJson* needs to be destructed by calling `ylong_json_delete`. + let null = ylong_json_create_null(); + ylong_json_delete(null); + } + } + + /// UT test for `ylong_json_duplicate`. + /// + /// # Title + /// ut_ylong_json_duplicate + /// + /// # Brief + /// 1. Calls `ylong_json_duplicate` to clone the value. + /// 2. Checks if the test results are correct. + #[test] + fn ut_ylong_json_duplicate() { + unsafe { + // Null ptr + let json = null_mut(); + assert!(ylong_json_duplicate(json, 0).is_null()); + + // Null ptr + let json = null_mut(); + assert!(ylong_json_duplicate(json, 1).is_null()); + + let str = str_to_c_char(JSON_TEXT); + let err = null_mut::(); + let json = ylong_json_parse(str, &err as *const *mut c_char as *mut *mut c_char); + assert!(err.is_null()); + assert!(!json.is_null()); + + // If recurse is 0, does not clone recursively. + let duplicate = ylong_json_duplicate(json, 0); + let result = ylong_json_print_unformatted(duplicate); + let result = CString::from_raw(result).into_string().unwrap(); + assert_eq!(result, "{}"); + // Destruction + let _ = Box::from_raw(str); + ylong_json_delete(duplicate); + + // If recurse is not 0, do recursive cloning. + let duplicate = ylong_json_duplicate(json, 1); + let result = ylong_json_print_unformatted(duplicate); + let result = CString::from_raw(result).into_string().unwrap(); + let origin = ylong_json_print_unformatted(json); + let origin = CString::from_raw(origin).into_string().unwrap(); + // The json address value is not equal to duplicate, + // which means it is a different instance. + assert_ne!(duplicate, json); + // But the output is the same. + assert_eq!(result, origin); + // Destruction + ylong_json_delete(duplicate); + ylong_json_delete(json); + } + } + + /// UT test for `ylong_json_create_null`. + /// + /// # Title + /// ut_ylong_json_create_null + /// + /// # Brief + /// 1. Calls `ylong_json_create_null` to create a null. + /// 2. Checks if the test results are correct. + #[test] + fn ut_ylong_json_create_null() { + unsafe { + let null = ylong_json_create_null(); + assert_eq!(ylong_json_is_null(null), 1); + ylong_json_delete(null); + } + } + + /// UT test for `ylong_json_is_null`. + /// + /// # Title + /// ut_ylong_json_is_null + /// + /// # Brief + /// 1. Calls `ylong_json_is_null` to determine whether the underlying structure is null. + /// 2. Checks if the test results are correct. + #[test] + fn ut_ylong_json_is_null() { + unsafe { + // Null ptr + let null = null_mut(); + assert_eq!(ylong_json_is_null(null), 0); + + // If the underlying structure is Null, returns true. + let null = ylong_json_create_null(); + assert_eq!(ylong_json_is_null(null), 1); + ylong_json_delete(null); + + // Else returns false. + let bool = ylong_json_create_bool(0xffff); + assert_eq!(ylong_json_is_null(bool), 0); + ylong_json_delete(bool); + } + } + + /// UT test for `ylong_json_create_bool`. + /// + /// # Title + /// ut_ylong_json_create_bool + /// + /// # Brief + /// 1. Calls `ylong_json_create_bool` to create a bool. + /// 2. Checks if the test results are correct. + #[test] + fn ut_ylong_json_create_bool() { + unsafe { + // Creates true. + let bool = ylong_json_create_bool(0xffff); + let mut val = 0; + ylong_json_get_value_from_bool(bool, &mut val as *mut c_int); + assert_eq!(val, 1); + ylong_json_delete(bool); + + // Creates false. + let bool = ylong_json_create_bool(0); + let mut val = 1; + ylong_json_get_value_from_bool(bool, &mut val as *mut c_int); + assert_eq!(val, 0); + ylong_json_delete(bool); + } + } + + /// UT test for `ylong_json_is_bool`. + /// + /// # Title + /// ut_ylong_json_is_bool + /// + /// # Brief + /// 1. Calls `ylong_json_is_bool` to determine whether the underlying structure is bool. + /// 2. Checks if the test results are correct. + #[test] + fn ut_ylong_json_is_bool() { + unsafe { + // Null ptr + let bool = null_mut(); + assert_eq!(ylong_json_is_bool(bool), 0); + + // True + let bool = ylong_json_create_bool(0xffff); + assert_eq!(ylong_json_is_bool(bool), 1); + ylong_json_delete(bool); + + // False + let bool = ylong_json_create_bool(0); + assert_eq!(ylong_json_is_bool(bool), 1); + ylong_json_delete(bool); + + // Non-bool case + let null = ylong_json_create_null(); + assert_eq!(ylong_json_is_bool(null), 0); + ylong_json_delete(null); + } + } + + /// UT test for `ylong_json_get_value_from_bool`. + /// + /// # Title + /// ut_ylong_json_get_value_from_bool + /// + /// # Brief + /// 1. Calls `ylong_json_get_value_from_bool` to get value from bool. + /// 2. Checks if the test results are correct. + #[test] + fn ut_ylong_json_get_value_from_bool() { + unsafe { + // Null ptr + let bool = null_mut(); + let mut val = 0i32; + assert_eq!( + ylong_json_get_value_from_bool(bool, &mut val as *mut c_int), + 0 + ); + + let bool = ylong_json_create_bool(0xffff); + let mut val = 0; + assert_eq!( + ylong_json_get_value_from_bool(bool, &mut val as *mut c_int), + 1 + ); + assert_eq!(val, 1); + ylong_json_delete(bool); + + let null = ylong_json_create_null(); + let mut val = 0; + assert_eq!( + ylong_json_get_value_from_bool(null, &mut val as *mut c_int), + 0 + ); + ylong_json_delete(null); + } + } + + /// UT test for `ylong_json_set_value_to_bool`. + /// + /// # Title + /// ut_ylong_json_set_value_to_bool + /// + /// # Brief + /// 1. Calls `ylong_json_set_value_to_bool` to set value to bool. + /// 2. Checks if the test results are correct. + #[test] + fn ut_ylong_json_set_value_to_bool() { + unsafe { + // Null ptr + let bool = null_mut(); + assert_eq!(ylong_json_set_value_to_bool(bool, 1), 0); + + let bool = ylong_json_create_bool(0xffff); + let mut val = 0; + assert_eq!( + ylong_json_get_value_from_bool(bool, &mut val as *mut c_int), + 1 + ); + assert_eq!(val, 1); + + assert_eq!(ylong_json_set_value_to_bool(bool, 0), 1); + assert_eq!( + ylong_json_get_value_from_bool(bool, &mut val as *mut c_int), + 1 + ); + assert_eq!(val, 0); + ylong_json_delete(bool); + + let null = ylong_json_create_null(); + assert_eq!(ylong_json_set_value_to_bool(null, 0), 0); + ylong_json_delete(null); + } + } + + /// UT test for `ylong_json_create_double_number`. + /// + /// # Title + /// ut_ylong_json_create_double_number + /// + /// # Brief + /// 1. Calls `ylong_json_create_double_number` to create a double number. + /// 2. Checks if the test results are correct. + #[test] + fn ut_ylong_json_create_double_number() { + unsafe { + let double = ylong_json_create_double_number(3.24); + let mut number = 0f64; + ylong_json_get_double_value_from_number(double, &mut number as *mut c_double); + assert_eq!(number, 3.24); + ylong_json_delete(double); + } + } + + /// UT test for `ylong_json_create_int_number`. + /// + /// # Title + /// ut_ylong_json_create_int_number + /// + /// # Brief + /// 1. Calls `ylong_json_create_int_number` to create a int number. + /// 2. Checks if the test results are correct. + #[test] + fn ut_ylong_json_create_int_number() { + unsafe { + let int = ylong_json_create_int_number(0xffff); + let mut number = 0i64; + ylong_json_get_int_value_from_number(int, &mut number as *mut c_longlong); + assert_eq!(number, 0xffff); + ylong_json_delete(int); + } + } + + /// UT test for `ylong_json_is_number`. + /// + /// # Title + /// ut_ylong_json_is_number + /// + /// # Brief + /// 1. Calls `ylong_json_is_number` to determine whether the value is number. + /// 2. Checks if the test results are correct. + #[test] + fn ut_ylong_json_is_number() { + unsafe { + // Null ptr + let number = null_mut(); + assert_eq!(ylong_json_is_number(number), 0); + + let int = ylong_json_create_int_number(1i64); + assert_eq!(ylong_json_is_number(int), 1); + ylong_json_delete(int); + + let double = ylong_json_create_double_number(3.24); + assert_eq!(ylong_json_is_number(double), 1); + ylong_json_delete(double); + + let null = ylong_json_create_null(); + assert_eq!(ylong_json_is_number(null), 0); + ylong_json_delete(null); + } + } + + /// UT test for `ylong_json_is_double_number`. + /// + /// # Title + /// ut_ylong_json_is_double_number + /// + /// # Brief + /// 1. Calls `ylong_json_is_double_number` to determine whether the value is double number. + /// 2. Checks if the test results are correct. + #[test] + fn ut_ylong_json_is_double_number() { + unsafe { + // Null ptr + let double = null_mut(); + assert_eq!(ylong_json_is_double_number(double), 0); + + let int = ylong_json_create_int_number(1i64); + assert_eq!(ylong_json_is_double_number(int), 0); + ylong_json_delete(int); + + let double = ylong_json_create_double_number(3.24); + assert_eq!(ylong_json_is_double_number(double), 1); + ylong_json_delete(double); + + let null = ylong_json_create_null(); + assert_eq!(ylong_json_is_double_number(null), 0); + ylong_json_delete(null); + } + } + + /// UT test for `ylong_json_is_int_number`. + /// + /// # Title + /// ut_ylong_json_is_int_number + /// + /// # Brief + /// 1. Calls `ylong_json_is_int_number` to determine whether the value is int number. + /// 2. Checks if the test results are correct. + #[test] + fn ut_ylong_json_is_int_number() { + unsafe { + // Null ptr + let int = null_mut(); + assert_eq!(ylong_json_is_int_number(int), 0); + + let int = ylong_json_create_int_number(1i64); + assert_eq!(ylong_json_is_int_number(int), 1); + ylong_json_delete(int); + + let double = ylong_json_create_double_number(3.24); + assert_eq!(ylong_json_is_int_number(double), 0); + ylong_json_delete(double); + + let null = ylong_json_create_null(); + assert_eq!(ylong_json_is_int_number(null), 0); + ylong_json_delete(null); + } + } + + /// UT test for `ylong_json_get_double_value_from_number`. + /// + /// # Title + /// ut_ylong_json_get_double_value_from_number + /// + /// # Brief + /// 1. Calls `ylong_json_get_double_value_from_number` to get double value from number. + /// 2. Checks if the test results are correct. + #[test] + fn ut_ylong_json_get_double_value_from_number() { + unsafe { + // Null ptr + let double = null_mut(); + let mut number = 0f64; + assert_eq!( + ylong_json_get_double_value_from_number(double, &mut number as *mut c_double), + 0 + ); + + let int = ylong_json_create_int_number(1i64); + let mut number = 0f64; + assert_eq!( + ylong_json_get_double_value_from_number(int, &mut number as *mut c_double), + 1 + ); + assert_eq!(number, 1.0); + ylong_json_delete(int); + + let double = ylong_json_create_double_number(3.24); + let mut number = 0f64; + assert_eq!( + ylong_json_get_double_value_from_number(double, &mut number as *mut c_double), + 1 + ); + assert_eq!(number, 3.24); + ylong_json_delete(double); + + let null = ylong_json_create_null(); + let mut number = 0f64; + assert_eq!( + ylong_json_get_double_value_from_number(null, &mut number as *mut c_double), + 0 + ); + ylong_json_delete(null); + } + } + + /// UT test for `ylong_json_get_int_value_from_number`. + /// + /// # Title + /// ut_ylong_json_get_int_value_from_number + /// + /// # Brief + /// 1. Calls `ylong_json_get_int_value_from_number` to get int value from number. + /// 2. Checks if the test results are correct. + #[test] + fn ut_ylong_json_get_int_value_from_number() { + unsafe { + // Null ptr + let int = null_mut(); + let mut number = 0i64; + assert_eq!( + ylong_json_get_int_value_from_number(int, &mut number as *mut c_longlong), + 0 + ); + + let int = ylong_json_create_int_number(1i64); + let mut number = 0i64; + assert_eq!( + ylong_json_get_int_value_from_number(int, &mut number as *mut c_longlong), + 1 + ); + assert_eq!(number, 1i64); + ylong_json_delete(int); + + let double = ylong_json_create_double_number(3.24); + let mut number = 0i64; + assert_eq!( + ylong_json_get_int_value_from_number(double, &mut number as *mut c_longlong), + 1 + ); + assert_eq!(number, 3i64); + ylong_json_delete(double); + + let null = ylong_json_create_null(); + let mut number = 0i64; + assert_eq!( + ylong_json_get_int_value_from_number(null, &mut number as *mut c_longlong), + 0 + ); + ylong_json_delete(null); + } + } + + /// UT test for `ylong_json_set_double_value_to_number`. + /// + /// # Title + /// ut_ylong_json_set_double_value_to_number + /// + /// # Brief + /// 1. Calls `ylong_json_set_double_value_to_number` to set double value to number. + /// 2. Checks if the test results are correct. + #[test] + fn ut_ylong_json_set_double_value_to_number() { + unsafe { + // Null ptr + let number = null_mut(); + assert_eq!(ylong_json_set_double_value_to_number(number, 3.24), 0); + + let double = ylong_json_create_double_number(3.24); + let mut number = 0f64; + ylong_json_get_double_value_from_number(double, &mut number as *mut c_double); + assert_eq!(number, 3.24); + assert_eq!(ylong_json_set_double_value_to_number(double, 1.23), 1); + ylong_json_get_double_value_from_number(double, &mut number as *mut c_double); + assert_eq!(number, 1.23); + ylong_json_delete(double); + + let int = ylong_json_create_int_number(1i64); + let mut number = 0i64; + ylong_json_get_int_value_from_number(int, &mut number as *mut c_longlong); + assert_eq!(number, 1i64); + assert_eq!(ylong_json_set_double_value_to_number(int, 3.24), 1); + ylong_json_get_int_value_from_number(int, &mut number as *mut c_longlong); + assert_eq!(number, 3i64); + ylong_json_delete(int); + + let null = ylong_json_create_null(); + assert_eq!(ylong_json_set_double_value_to_number(null, 3.24), 0); + ylong_json_delete(null); + } + } + + /// UT test for `ylong_json_set_int_value_to_number`. + /// + /// # Title + /// ut_ylong_json_set_int_value_to_number + /// + /// # Brief + /// 1. Calls `ylong_json_set_int_value_to_number` to set int value to number. + /// 2. Checks if the test results are correct. + #[test] + fn ut_ylong_json_set_int_value_to_number() { + unsafe { + // Null ptr + let number = null_mut(); + assert_eq!(ylong_json_set_int_value_to_number(number, 1), 0); + + let int = ylong_json_create_int_number(1i64); + let mut number = 0i64; + ylong_json_get_int_value_from_number(int, &mut number as *mut c_longlong); + assert_eq!(number, 1i64); + assert_eq!(ylong_json_set_int_value_to_number(int, 3i64), 1); + ylong_json_get_int_value_from_number(int, &mut number as *mut c_longlong); + assert_eq!(number, 3i64); + ylong_json_delete(int); + + let double = ylong_json_create_double_number(3.24); + let mut number = 0f64; + ylong_json_get_double_value_from_number(double, &mut number as *mut c_double); + assert_eq!(number, 3.24); + assert_eq!(ylong_json_set_int_value_to_number(double, 1), 1); + ylong_json_get_double_value_from_number(double, &mut number as *mut c_double); + assert_eq!(number, 1.0); + ylong_json_delete(double); + + let null = ylong_json_create_null(); + assert_eq!(ylong_json_set_int_value_to_number(null, 1), 0); + ylong_json_delete(null); + } + } + + /// UT test for `ylong_json_create_string`. + /// + /// # Title + /// ut_ylong_json_create_string + /// + /// # Brief + /// 1. Calls `ylong_json_create_string` to create a string from *mut c_char. + /// 2. Checks if the test results are correct. + #[test] + fn ut_ylong_json_create_string() { + unsafe { + // Null ptr + let str = null(); + assert!(ylong_json_create_string(str).is_null()); + + let str = str_to_c_char("Hello World"); + let string = ylong_json_create_string(str); + let mut content = null_mut(); + ylong_json_get_value_from_string(string, &mut content as *mut *mut c_char); + let result = String::from_utf8_unchecked(CStr::from_ptr(content).to_bytes().to_vec()); + assert_eq!(result, "Hello World"); + // Destruction + let _ = Box::from_raw(str); + ylong_json_delete(string); + } + } + + /// UT test for `ylong_json_is_string`. + /// + /// # Title + /// ut_ylong_json_is_string + /// + /// # Brief + /// 1. Calls `ylong_json_is_string` to determine whether the value is string. + /// 2. Checks if the test results are correct. + #[test] + fn ut_ylong_json_is_string() { + unsafe { + // Null ptr + let string = null_mut(); + assert_eq!(ylong_json_is_string(string), 0); + + let str = str_to_c_char("Hello World"); + let string = ylong_json_create_string(str); + assert_eq!(ylong_json_is_string(string), 1); + let _ = Box::from_raw(str); + ylong_json_delete(string); + + let null = ylong_json_create_null(); + assert_eq!(ylong_json_is_string(null), 0); + ylong_json_delete(null); + } + } + + /// UT test for `ylong_json_get_value_from_string`. + /// + /// # Title + /// ut_ylong_json_get_value_from_string + /// + /// # Brief + /// 1. Calls `ylong_json_get_value_from_string` to get value from string. + /// 2. Checks if the test results are correct. + #[test] + fn ut_ylong_json_get_value_from_string() { + unsafe { + // Null ptr + let string = null_mut(); + let mut str = null_mut(); + assert_eq!( + ylong_json_get_value_from_string(string, &mut str as *mut *mut c_char), + 0 + ); + + let str = str_to_c_char("Hello World"); + let string = ylong_json_create_string(str); + let mut content = null_mut(); + assert_eq!( + ylong_json_get_value_from_string(string, &mut content as *mut *mut c_char), + 1 + ); + let result = String::from_utf8_unchecked(CStr::from_ptr(content).to_bytes().to_vec()); + assert_eq!(result, "Hello World"); + let _ = Box::from_raw(str); + ylong_json_delete(string); + + let null = ylong_json_create_null(); + let mut content = null_mut(); + assert_eq!( + ylong_json_get_value_from_string(null, &mut content as *mut *mut c_char), + 0 + ); + ylong_json_delete(null); + } + } + + /// UT test for `ylong_json_set_value_to_string`. + /// + /// # Title + /// ut_ylong_json_set_value_to_string + /// + /// # Brief + /// 1. Calls `ylong_json_set_value_to_string` to set value to string. + /// 2. Checks if the test results are correct. + #[test] + fn ut_ylong_json_set_value_to_string() { + unsafe { + // Null ptr + let string = null_mut(); + let str = str_to_c_char("Hello World"); + assert_eq!(ylong_json_set_value_to_string(string, str), 0); + let _ = Box::from_raw(str); + + // Null ptr + let str = str_to_c_char("Hello World"); + let string = ylong_json_create_string(str); + let _ = Box::from_raw(str); + let str = null(); + assert_eq!(ylong_json_set_value_to_string(string, str), 0); + ylong_json_delete(string); + + let str = str_to_c_char("Hello World"); + let string = ylong_json_create_string(str); + // Check if the original value is "Hello World". + let mut content = null_mut(); + ylong_json_get_value_from_string(string, &mut content as *mut *mut c_char); + let result = String::from_utf8_unchecked(CStr::from_ptr(content).to_bytes().to_vec()); + assert_eq!(result, "Hello World"); + let _ = Box::from_raw(str); + // Use the function to set the content to "New String". + let str = str_to_c_char("New String"); + assert_eq!(ylong_json_set_value_to_string(string, str), 1); + // Check whether the Settings are successful. + ylong_json_get_value_from_string(string, &mut content as *mut *mut c_char); + let result = String::from_utf8_unchecked(CStr::from_ptr(content).to_bytes().to_vec()); + assert_eq!(result, "New String"); + let _ = Box::from_raw(str); + ylong_json_delete(string); + + let null = ylong_json_create_null(); + let str = str_to_c_char("New String"); + assert_eq!(ylong_json_set_value_to_string(null, str), 0); + let _ = Box::from_raw(str); + ylong_json_delete(null); + } + } + + /// UT test for `ylong_json_create_array`. + /// + /// # Title + /// ut_ylong_json_create_array + /// + /// # Brief + /// 1. Calls `ylong_json_create_array` to create an array. + /// 2. Checks if the test results are correct. + #[test] + fn ut_ylong_json_create_array() { + unsafe { + let array = ylong_json_create_array(); + let result = ylong_json_print_unformatted(array); + let result = CString::from_raw(result).into_string().unwrap(); + assert_eq!(result, "[]"); + ylong_json_delete(array); + } + } + + /// UT test for `ut_ylong_json_is_array`. + /// + /// # Title + /// ut_ylong_json_is_array + /// + /// # Brief + /// 1. Calls `long_json_is_array` to determine whether the value is an array. + /// 2. Checks if the test results are correct. + #[test] + fn ut_ylong_json_is_array() { + unsafe { + // Null ptr + let array = null_mut(); + assert_eq!(ylong_json_is_array(array), 0); + + let array = ylong_json_create_array(); + assert_eq!(ylong_json_is_array(array), 1); + ylong_json_delete(array); + + let null = ylong_json_create_null(); + assert_eq!(ylong_json_is_array(null), 0); + ylong_json_delete(null); + } + } + + /// UT test for `ylong_json_get_array_size`. + /// + /// # Title + /// ut_ylong_json_get_array_size + /// + /// # Brief + /// 1. Calls `ylong_json_get_array_size` to get size of the array. + /// 2. Checks if the test results are correct. + #[test] + fn ut_ylong_json_get_array_size() { + unsafe { + // Null ptr + let array = null_mut(); + let mut len = 0i32; + assert_eq!(ylong_json_get_array_size(array, &mut len as *mut c_int), 0); + + // Null ptr + let array = ylong_json_create_array(); + let len = null_mut(); + assert_eq!(ylong_json_get_array_size(array, len), 0); + ylong_json_delete(array); + + let array = ylong_json_create_array(); + let mut len = 1i32; + assert_eq!(ylong_json_get_array_size(array, &mut len as *mut c_int), 1); + assert_eq!(len, 0); + ylong_json_delete(array); + + let null = ylong_json_create_null(); + let mut len = 1i32; + assert_eq!(ylong_json_get_array_size(null, &mut len as *mut c_int), 0); + ylong_json_delete(null); + } + } + + /// UT test for `ylong_json_get_array_item`. + /// + /// # Title + /// ut_ylong_json_get_array_item + /// + /// # Brief + /// 1. Calls `ylong_json_get_array_item` to get an item of the array. + /// 2. Checks if the test results are correct. + #[test] + fn ut_ylong_json_get_array_item() { + unsafe { + // Null ptr + let array = null_mut(); + assert!(ylong_json_get_array_item(array, 0).is_null()); + + const TEXT: &str = "[null, 1.0, true, \"Test\"]"; + let str = str_to_c_char(TEXT); + let mut msg = null_mut(); + let array = ylong_json_parse(str, &mut msg as *mut *mut c_char); + let _ = Box::from_raw(str); + assert_eq!(ylong_json_is_array(array), 1); + + let item0 = ylong_json_get_array_item(array, 0); + assert_eq!(ylong_json_is_null(item0), 1); + + let item1 = ylong_json_get_array_item(array, 1); + assert_eq!(ylong_json_is_double_number(item1), 1); + let mut number = 0f64; + assert_eq!( + ylong_json_get_double_value_from_number(item1, &mut number as *mut c_double), + 1 + ); + assert_eq!(number, 1.0); + + let item2 = ylong_json_get_array_item(array, 2); + assert_eq!(ylong_json_is_bool(item2), 1); + let mut bool = 0i32; + assert_eq!( + ylong_json_get_value_from_bool(item2, &mut bool as *mut c_int), + 1 + ); + assert_eq!(bool, 1i32); + + let item3 = ylong_json_get_array_item(array, 3); + assert_eq!(ylong_json_is_string(item3), 1); + let mut content = null_mut(); + assert_eq!( + ylong_json_get_value_from_string(item3, &mut content as *mut *mut c_char), + 1 + ); + let result = String::from_utf8_unchecked(CStr::from_ptr(content).to_bytes().to_vec()); + assert_eq!(result, "Test"); + + assert!(ylong_json_get_array_item(array, 4).is_null()); + + ylong_json_delete(array); + + let null = ylong_json_create_null(); + assert!(ylong_json_get_array_item(null, 0).is_null()); + ylong_json_delete(null); + } + } + + /// UT test for `ylong_json_add_item_to_array`. + /// + /// # Title + /// ut_ylong_json_add_item_to_array + /// + /// # Brief + /// 1. Calls `ylong_json_add_item_to_array` to add an item to the array. + /// 2. Checks if the test results are correct. + #[test] + fn ut_ylong_json_add_item_to_array() { + unsafe { + // Null ptr + let array = null_mut(); + let item = ylong_json_create_null(); + assert_eq!(ylong_json_add_item_to_array(array, item), 0); + ylong_json_delete(item); + + let array = ylong_json_create_array(); + let item = null_mut(); + assert_eq!(ylong_json_add_item_to_array(array, item), 0); + ylong_json_delete(array); + + let array = ylong_json_create_array(); + let null = ylong_json_create_null(); + assert_eq!(ylong_json_add_item_to_array(array, null), 1); + let result = ylong_json_print_unformatted(array); + let result = CString::from_raw(result).into_string().unwrap(); + assert_eq!(result, "[null]"); + ylong_json_delete(array); + + let null = ylong_json_create_null(); + let null2 = ylong_json_create_null(); + assert_eq!(ylong_json_add_item_to_array(null, null2), 0); + ylong_json_delete(null); + ylong_json_delete(null2); + } + } + + /// UT test for `ylong_json_replace_item_in_array`. + /// + /// # Title + /// ut_ylong_json_replace_item_in_array + /// + /// # Brief + /// 1. Calls `ylong_json_replace_item_in_array` to replace an item in the array. + /// 2. Checks if the test results are correct. + #[test] + fn ut_ylong_json_replace_item_in_array() { + unsafe { + // Null ptr + let array = null_mut(); + let item = ylong_json_create_null(); + assert_eq!(ylong_json_replace_array_item_by_index(array, 0, item), 0); + ylong_json_delete(item); + + // Null ptr + let array = ylong_json_create_array(); + let item = null_mut(); + assert_eq!(ylong_json_replace_array_item_by_index(array, 0, item), 0); + ylong_json_delete(array); + + let array = ylong_json_create_array(); + let null = ylong_json_create_null(); + assert_eq!(ylong_json_add_item_to_array(array, null), 1); + let result = ylong_json_print_unformatted(array); + let result = CString::from_raw(result).into_string().unwrap(); + assert_eq!(result, "[null]"); + let replace = ylong_json_create_bool(1); + assert_eq!(ylong_json_replace_array_item_by_index(array, 0, replace), 1); + let result = ylong_json_print_unformatted(array); + let result = CString::from_raw(result).into_string().unwrap(); + assert_eq!(result, "[true]"); + ylong_json_delete(array); + + let null = ylong_json_create_null(); + let null2 = ylong_json_create_null(); + assert_eq!(ylong_json_replace_array_item_by_index(null, 0, null2), 0); + ylong_json_delete(null); + ylong_json_delete(null2); + } + } + + /// UT test for `ylong_json_remove_array_item_by_index`. + /// + /// # Title + /// ut_ylong_json_remove_array_item_by_index + /// + /// # Brief + /// 1. Calls `ylong_json_remove_array_item_by_index` to remove an item in the array by index. + /// (Uses the method 'remove' of Array.) + /// 2. Checks if the test results are correct. + #[test] + fn ut_ylong_json_remove_array_item_by_index() { + unsafe { + // Null ptr + let array = null_mut(); + assert!(ylong_json_remove_array_item_by_index(array, 0).is_null()); + + const TEXT: &str = "[null, 1.0, true, \"Test\"]"; + let str = str_to_c_char(TEXT); + let mut msg = null_mut(); + let array = ylong_json_parse(str, &mut msg as *mut *mut c_char); + let _ = Box::from_raw(str); + let mut len = 0i32; + assert_eq!(ylong_json_get_array_size(array, &mut len as *mut c_int), 1); + assert_eq!(len, 4); + + let item0 = ylong_json_remove_array_item_by_index(array, 0); + assert_eq!(ylong_json_is_null(item0), 1); + assert_eq!(ylong_json_get_array_size(array, &mut len as *mut c_int), 1); + assert_eq!(len, 3); + ylong_json_delete(item0); + + let item3 = ylong_json_remove_array_item_by_index(array, 2); + assert_eq!(ylong_json_is_string(item3), 1); + assert_eq!(ylong_json_get_array_size(array, &mut len as *mut c_int), 1); + assert_eq!(len, 2); + ylong_json_delete(item3); + + let item1 = ylong_json_remove_array_item_by_index(array, 0); + assert_eq!(ylong_json_is_number(item1), 1); + assert_eq!(ylong_json_get_array_size(array, &mut len as *mut c_int), 1); + assert_eq!(len, 1); + ylong_json_delete(item1); + + let item2 = ylong_json_remove_array_item_by_index(array, 0); + assert_eq!(ylong_json_is_bool(item2), 1); + assert_eq!(ylong_json_get_array_size(array, &mut len as *mut c_int), 1); + assert_eq!(len, 0); + ylong_json_delete(item2); + ylong_json_delete(array); + + let null = ylong_json_create_null(); + let item = ylong_json_remove_array_item_by_index(null, 0); + assert!(item.is_null()); + ylong_json_delete(null); + } + } + + /// UT test for `ylong_json_delete_array_item_by_index`. + /// + /// # Title + /// ut_ylong_json_delete_array_item_by_index + /// + /// # Brief + /// 1. Calls `ylong_json_delete_array_item_by_index` to delete an item in the array by index. + /// (Uses the method 'remove' of underlying data structure.) + /// 2. Checks if the test results are correct. + #[test] + fn ut_ylong_json_delete_array_item_by_index() { + unsafe { + // Null ptr scene, if the process does not exit abnormally, it is successful. + let array = null_mut(); + ylong_json_delete_array_item_by_index(array, 0); + + const TEXT: &str = "[null, 1.0, true, \"Test\"]"; + let str = str_to_c_char(TEXT); + let mut msg = null_mut(); + let array = ylong_json_parse(str, &mut msg as *mut *mut c_char); + let mut len = 0i32; + assert_eq!(ylong_json_get_array_size(array, &mut len as *mut c_int), 1); + assert_eq!(len, 4); + + ylong_json_delete_array_item_by_index(array, 0); + assert_eq!(ylong_json_get_array_size(array, &mut len as *mut c_int), 1); + assert_eq!(len, 3); + + ylong_json_delete_array_item_by_index(array, 0); + assert_eq!(ylong_json_get_array_size(array, &mut len as *mut c_int), 1); + assert_eq!(len, 2); + + ylong_json_delete_array_item_by_index(array, 0); + assert_eq!(ylong_json_get_array_size(array, &mut len as *mut c_int), 1); + assert_eq!(len, 1); + + ylong_json_delete_array_item_by_index(array, 0); + assert_eq!(ylong_json_get_array_size(array, &mut len as *mut c_int), 1); + assert_eq!(len, 0); + + let _ = Box::from_raw(str); + ylong_json_delete(array); + + let null = ylong_json_create_null(); + ylong_json_delete_array_item_by_index(null, 0); + ylong_json_delete(null); + } + } + + /// UT test for `ylong_json_get_array_node`. + /// + /// # Title + /// ut_ylong_json_get_array_node + /// + /// # Brief + /// 1. Calls `ylong_json_get_array_node` to get an array node. + /// 2. Checks if the test results are correct. + #[cfg(feature = "list_array")] + #[test] + fn ut_ylong_json_get_array_node() { + unsafe { + // Null ptr + let array = null_mut(); + assert!(ylong_json_get_array_node(array, 0).is_null()); + + const TEXT: &str = "[null, 1.0, true, \"Test\"]"; + let str = str_to_c_char(TEXT); + let mut msg = null_mut(); + let array = ylong_json_parse(str, &mut msg as *mut *mut c_char); + let _ = Box::from_raw(str); + assert_eq!(ylong_json_is_array(array), 1); + + let node0 = ylong_json_get_array_node(array, 0); + assert!(!node0.is_null()); + + let node1 = ylong_json_get_array_node(array, 1); + assert!(!node1.is_null()); + + let node2 = ylong_json_get_array_node(array, 2); + assert!(!node2.is_null()); + + let node3 = ylong_json_get_array_node(array, 3); + assert!(!node3.is_null()); + + let node4 = ylong_json_get_array_node(array, 4); + assert!(node4.is_null()); + + ylong_json_delete(array); + + let null = ylong_json_create_null(); + assert!(ylong_json_get_array_node(null, 0).is_null()); + ylong_json_delete(null); + } + } + + /// UT test for `ylong_json_get_item_from_array_node`. + /// + /// # Title + /// ut_ylong_json_get_item_from_array_node + /// + /// # Brief + /// 1. Calls `ylong_json_get_item_from_array_node` to get the item of an array node. + /// 2. Checks if the test results are correct. + #[cfg(feature = "list_array")] + #[test] + fn ut_ylong_json_get_item_from_array_node() { + unsafe { + // Null ptr + let node = null_mut(); + assert!(ylong_json_get_array_node(node, 0).is_null()); + + const TEXT: &str = "[null, 1.0, true, \"Test\"]"; + let str = str_to_c_char(TEXT); + let mut msg = null_mut(); + let array = ylong_json_parse(str, &mut msg as *mut *mut c_char); + let _ = Box::from_raw(str); + assert_eq!(ylong_json_is_array(array), 1); + + let node0 = ylong_json_get_array_node(array, 0); + assert!(!node0.is_null()); + let item0 = ylong_json_get_item_from_array_node(node0); + assert_eq!(ylong_json_is_null(item0), 1); + + let node1 = ylong_json_get_array_node(array, 1); + assert!(!node1.is_null()); + let item1 = ylong_json_get_item_from_array_node(node1); + let mut number = 0f64; + assert_eq!( + ylong_json_get_double_value_from_number(item1, &mut number as *mut c_double), + 1 + ); + assert_eq!(number, 1.0); + + let node2 = ylong_json_get_array_node(array, 2); + assert!(!node2.is_null()); + let item2 = ylong_json_get_item_from_array_node(node2); + let mut bool = 0i32; + assert_eq!( + ylong_json_get_value_from_bool(item2, &mut bool as *mut c_int), + 1 + ); + assert_eq!(bool, 1i32); + + let node3 = ylong_json_get_array_node(array, 3); + assert!(!node3.is_null()); + let item3 = ylong_json_get_item_from_array_node(node3); + let mut content = null_mut(); + assert_eq!( + ylong_json_get_value_from_string(item3, &mut content as *mut *mut c_char), + 1 + ); + let result = String::from_utf8_unchecked(CStr::from_ptr(content).to_bytes().to_vec()); + assert_eq!(result, "Test"); + + ylong_json_delete(array); + } + } + + /// UT test for `ylong_json_add_item_to_array_then_get_node`. + /// + /// # Title + /// ut_ylong_json_add_item_to_array_then_get_node + /// + /// # Brief + /// 1. Calls `ylong_json_add_item_to_array_then_get_node` to add an item to array and get the corresponding node. + /// 2. Checks if the test results are correct. + #[cfg(feature = "list_array")] + #[test] + fn ut_ylong_json_add_item_to_array_then_get_node() { + unsafe { + // Null ptr + let array = null_mut(); + let item = ylong_json_create_null(); + assert!(ylong_json_add_item_to_array_then_get_node(array, item).is_null()); + ylong_json_delete(item); + + // Null ptr + let array = ylong_json_create_array(); + let item = null_mut(); + assert!(ylong_json_add_item_to_array_then_get_node(array, item).is_null()); + ylong_json_delete(array); + + let array = ylong_json_create_array(); + let null = ylong_json_create_null(); + let node0 = ylong_json_add_item_to_array_then_get_node(array, null); + assert!(!node0.is_null()); + let mut len = 0i32; + assert_eq!(ylong_json_get_array_size(array, &mut len as *mut c_int), 1); + assert_eq!(len, 1); + let item0 = ylong_json_get_item_from_array_node(node0); + assert!(!item0.is_null()); + assert_eq!(ylong_json_is_null(item0), 1); + let item0 = ylong_json_remove_array_node(node0); + ylong_json_delete(item0); + assert_eq!(ylong_json_get_array_size(array, &mut len as *mut c_int), 1); + assert_eq!(len, 0); + ylong_json_delete(array); + } + } + + /// UT test for `ylong_json_replace_item_of_array_node`. + /// + /// # Title + /// ut_ylong_json_replace_item_of_array_node + /// + /// # Brief + /// 1. Calls `ylong_json_replace_item_of_array_node` to replace the item of an array node. + /// 2. Checks if the test results are correct. + #[cfg(feature = "list_array")] + #[test] + fn ut_ylong_json_replace_item_of_array_node() { + unsafe { + // Null ptr + let node = null_mut(); + let item = ylong_json_create_null(); + assert_eq!(ylong_json_replace_item_of_array_node(node, item), 0); + ylong_json_delete(item); + + // Null ptr scene, if the process does not exit abnormally, it is successful. + let array = ylong_json_create_array(); + let null = ylong_json_create_null(); + let node = ylong_json_add_item_to_array_then_get_node(array, null); + let item = null_mut(); + assert_eq!(ylong_json_replace_item_of_array_node(node, item), 0); + ylong_json_delete(array); + + let array = ylong_json_create_array(); + let null = ylong_json_create_null(); + let node = ylong_json_add_item_to_array_then_get_node(array, null); + let result = ylong_json_print_unformatted(array); + let result = CString::from_raw(result).into_string().unwrap(); + assert_eq!(result, "[null]"); + + let bool = ylong_json_create_bool(1); + assert_eq!(ylong_json_replace_item_of_array_node(node, bool), 1); + let result = ylong_json_print_unformatted(array); + let result = CString::from_raw(result).into_string().unwrap(); + assert_eq!(result, "[true]"); + + ylong_json_delete(array); + } + } + + /// UT test for `ylong_json_remove_array_node`. + /// + /// # Title + /// ut_ylong_json_remove_array_node + /// + /// # Brief + /// 1. Calls `ylong_json_remove_array_node` to remove an array node. + /// 2. Checks if the test results are correct. + #[cfg(feature = "list_array")] + #[test] + fn ut_ylong_json_remove_array_node() { + unsafe { + // Null ptr + let node = null_mut(); + assert!(ylong_json_remove_array_node(node).is_null()); + + const TEXT: &str = "[null, 1.0, true, \"Test\"]"; + let str = str_to_c_char(TEXT); + let mut msg = null_mut(); + let array = ylong_json_parse(str, &mut msg as *mut *mut c_char); + let _ = Box::from_raw(str); + assert_eq!(ylong_json_is_array(array), 1); + let mut len = 0i32; + assert_eq!(ylong_json_get_array_size(array, &mut len as *mut c_int), 1); + assert_eq!(len, 4); + + let node0 = ylong_json_get_array_node(array, 0); + assert!(!node0.is_null()); + let item0 = ylong_json_remove_array_node(node0); + assert!(!item0.is_null()); + assert_eq!(ylong_json_get_array_size(array, &mut len as *mut c_int), 1); + assert_eq!(len, 3); + assert_eq!(ylong_json_is_null(item0), 1); + ylong_json_delete(item0); + + let node0 = ylong_json_get_array_node(array, 0); + assert!(!node0.is_null()); + let item0 = ylong_json_remove_array_node(node0); + assert!(!item0.is_null()); + assert_eq!(ylong_json_get_array_size(array, &mut len as *mut c_int), 1); + assert_eq!(len, 2); + let mut number = 0f64; + assert_eq!( + ylong_json_get_double_value_from_number(item0, &mut number as *mut c_double), + 1 + ); + assert_eq!(number, 1.0); + ylong_json_delete(item0); + + let node0 = ylong_json_get_array_node(array, 0); + assert!(!node0.is_null()); + let item0 = ylong_json_remove_array_node(node0); + assert!(!item0.is_null()); + assert_eq!(ylong_json_get_array_size(array, &mut len as *mut c_int), 1); + assert_eq!(len, 1); + let mut bool = 0i32; + assert_eq!( + ylong_json_get_value_from_bool(item0, &mut bool as *mut c_int), + 1 + ); + assert_eq!(bool, 1i32); + ylong_json_delete(item0); + + let node0 = ylong_json_get_array_node(array, 0); + assert!(!node0.is_null()); + let item0 = ylong_json_remove_array_node(node0); + assert!(!item0.is_null()); + assert_eq!(ylong_json_get_array_size(array, &mut len as *mut c_int), 1); + assert_eq!(len, 0); + let mut content = null_mut(); + assert_eq!( + ylong_json_get_value_from_string(item0, &mut content as *mut *mut c_char), + 1 + ); + let result = String::from_utf8_unchecked(CStr::from_ptr(content).to_bytes().to_vec()); + assert_eq!(result, "Test"); + ylong_json_delete(item0); + + ylong_json_delete(array); + } + } + + /// UT test for `ylong_json_delete_array_node`. + /// + /// # Title + /// ut_ylong_json_delete_array_node + /// + /// # Brief + /// 1. Calls `ylong_json_delete_array_node` to delete an array node. + /// 2. Checks if the test results are correct. + #[cfg(feature = "list_array")] + #[test] + fn ut_ylong_json_delete_array_node() { + unsafe { + // Null ptr scene, if the process does not exit abnormally, it is successful. + let node = null_mut(); + ylong_json_delete_array_node(node); + + const TEXT: &str = "[null, 1.0, true, \"Test\"]"; + let str = str_to_c_char(TEXT); + let mut msg = null_mut(); + let array = ylong_json_parse(str, &mut msg as *mut *mut c_char); + let _ = Box::from_raw(str); + let mut len = 0i32; + assert_eq!(ylong_json_get_array_size(array, &mut len as *mut c_int), 1); + assert_eq!(len, 4); + + let node0 = ylong_json_get_array_node(array, 0); + ylong_json_delete_array_node(node0); + assert_eq!(ylong_json_get_array_size(array, &mut len as *mut c_int), 1); + assert_eq!(len, 3); + + let node1 = ylong_json_get_array_node(array, 0); + ylong_json_delete_array_node(node1); + assert_eq!(ylong_json_get_array_size(array, &mut len as *mut c_int), 1); + assert_eq!(len, 2); + + let node2 = ylong_json_get_array_node(array, 0); + ylong_json_delete_array_node(node2); + assert_eq!(ylong_json_get_array_size(array, &mut len as *mut c_int), 1); + assert_eq!(len, 1); + + let node3 = ylong_json_get_array_node(array, 0); + ylong_json_delete_array_node(node3); + assert_eq!(ylong_json_get_array_size(array, &mut len as *mut c_int), 1); + assert_eq!(len, 0); + + ylong_json_delete(array); + } + } + + /// UT test for `ylong_json_create_object`. + /// + /// # Title + /// ut_ylong_json_create_object + /// + /// # Brief + /// 1. Calls `ylong_json_create_object` to create an object. + /// 2. Checks if the test results are correct. + #[test] + fn ut_ylong_json_create_object() { + unsafe { + let object = ylong_json_create_object(); + assert_eq!(ylong_json_is_object(object), 1); + let result = ylong_json_print_unformatted(object); + let result = CString::from_raw(result).into_string().unwrap(); + assert_eq!(result, "{}"); + ylong_json_delete(object); + } + } + + /// UT test for `ylong_json_is_object`. + /// + /// # Title + /// ut_ylong_json_is_object + /// + /// # Brief + /// 1. Calls `ylong_json_is_object` to determine whether the value is object. + /// 2. Checks if the test results are correct. + #[test] + fn ut_ylong_json_is_object() { + unsafe { + // Null ptr + let object = null_mut(); + assert_eq!(ylong_json_is_object(object), 0); + + let object = ylong_json_create_object(); + assert_eq!(ylong_json_is_object(object), 1); + ylong_json_delete(object); + + let null = ylong_json_create_null(); + assert_eq!(ylong_json_is_object(null), 0); + ylong_json_delete(null); + } + } + + /// UT test for `ylong_json_get_object_size`. + /// + /// # Title + /// ut_ylong_json_get_object_size + /// + /// # Brief + /// 1. Calls `ylong_json_get_object_size` to get size of an object. + /// 2. Checks if the test results are correct. + #[test] + fn ut_ylong_json_get_object_size() { + unsafe { + // Null ptr + let object = null_mut(); + let mut len = 0i32; + assert_eq!( + ylong_json_get_object_size(object, &mut len as *mut c_int), + 0 + ); + + // Null ptr + let object = ylong_json_create_object(); + let len = null_mut(); + assert_eq!(ylong_json_get_object_size(object, len), 0); + ylong_json_delete(object); + + let object = ylong_json_create_object(); + let mut len = 1i32; + assert_eq!( + ylong_json_get_object_size(object, &mut len as *mut c_int), + 1 + ); + assert_eq!(len, 0); + ylong_json_delete(object); + + let null = ylong_json_create_null(); + let mut len = 0i32; + assert_eq!(ylong_json_get_object_size(null, &mut len as *mut c_int), 0); + ylong_json_delete(null); + } + } + + /// UT test for `ylong_json_has_object_item`. + /// + /// # Title + /// ut_ylong_json_has_object_item + /// + /// # Brief + /// 1. Calls `ylong_json_has_object_item` to determine whether the item exists in the object. + /// 2. Checks if the test results are correct. + #[test] + fn ut_ylong_json_has_object_item() { + unsafe { + // Null ptr + let object = null_mut(); + let str = str_to_c_char("Hello World"); + assert_eq!(ylong_json_has_object_item(object, str), 0); + let _ = Box::from_raw(str); + + // Null ptr + let object = ylong_json_create_object(); + let str = null(); + assert_eq!(ylong_json_has_object_item(object, str), 0); + ylong_json_delete(object); + + const TEXT: &str = "{\"null\":null}"; + let str = str_to_c_char(TEXT); + let mut msg = null_mut(); + let object = ylong_json_parse(str, &mut msg as *mut *mut c_char); + let _ = Box::from_raw(str); + + let str = str_to_c_char("null"); + assert_eq!(ylong_json_has_object_item(object, str), 1); + let _ = Box::from_raw(str); + + let str = str_to_c_char("no_such_key"); + assert_eq!(ylong_json_has_object_item(object, str), 0); + let _ = Box::from_raw(str); + + ylong_json_delete(object); + + let null = ylong_json_create_null(); + let str = str_to_c_char("Invalid"); + assert_eq!(ylong_json_has_object_item(null, str), 0); + let _ = Box::from_raw(str); + ylong_json_delete(null); + } + } + + /// UT test for `ylong_json_get_object_item`. + /// + /// # Title + /// ut_ylong_json_get_object_item + /// + /// # Brief + /// 1. Calls `ylong_json_get_object_item` to get an item in the object. + /// 2. Checks if the test results are correct. + #[test] + fn ut_ylong_json_get_object_item() { + unsafe { + // Null ptr + let object = null_mut(); + let str = str_to_c_char("Hello World"); + assert!(ylong_json_get_object_item(object, str).is_null()); + let _ = Box::from_raw(str); + + // Null ptr + let object = ylong_json_create_object(); + let str = null(); + assert!(ylong_json_get_object_item(object, str).is_null()); + ylong_json_delete(object); + + const TEXT: &str = "{\"null\":null}"; + let str = str_to_c_char(TEXT); + let mut msg = null_mut(); + let object = ylong_json_parse(str, &mut msg as *mut *mut c_char); + let _ = Box::from_raw(str); + + let str = str_to_c_char("null"); + let item = ylong_json_get_object_item(object, str); + assert_eq!(ylong_json_is_null(item), 1); + let _ = Box::from_raw(str); + + let str = str_to_c_char("no_such_key"); + let item = ylong_json_get_object_item(object, str); + assert!(item.is_null()); + let _ = Box::from_raw(str); + + ylong_json_delete(object); + + let null = ylong_json_create_null(); + let str = str_to_c_char("Invalid"); + assert!(ylong_json_get_object_item(null, str).is_null()); + let _ = Box::from_raw(str); + ylong_json_delete(null); + } + } + + /// UT test for `ylong_json_add_item_to_object`. + /// + /// # Title + /// ut_ylong_json_add_item_to_object + /// + /// # Brief + /// 1. Calls `ylong_json_add_item_to_object` to add an item to the object. + /// 2. Checks if the test results are correct. + #[test] + fn ut_ylong_json_add_item_to_object() { + unsafe { + // Null ptr + let object = null_mut(); + let str = str_to_c_char("Hello World"); + let item = ylong_json_create_null(); + assert_eq!(ylong_json_add_item_to_object(object, str, item), 0); + let _ = Box::from_raw(str); + ylong_json_delete(item); + + // Null ptr + let object = ylong_json_create_object(); + let str = null(); + let item = ylong_json_create_null(); + assert_eq!(ylong_json_add_item_to_object(object, str, item), 0); + ylong_json_delete(object); + ylong_json_delete(item); + + // Null ptr + let object = ylong_json_create_object(); + let str = str_to_c_char("Hello World"); + let item = null_mut(); + assert_eq!(ylong_json_add_item_to_object(object, str, item), 0); + ylong_json_delete(object); + let _ = Box::from_raw(str); + + let object = ylong_json_create_object(); + let mut len = 0i32; + assert_eq!( + ylong_json_get_object_size(object, &mut len as *mut c_int), + 1 + ); + assert_eq!(len, 0); + let str = str_to_c_char("Hello World"); + let item = ylong_json_create_null(); + assert_eq!(ylong_json_add_item_to_object(object, str, item), 1); + let _ = Box::from_raw(str); + assert_eq!( + ylong_json_get_object_size(object, &mut len as *mut c_int), + 1 + ); + assert_eq!(len, 1); + ylong_json_delete(object); + + let null = ylong_json_create_null(); + let str = str_to_c_char("Hello World"); + let item = ylong_json_create_null(); + assert_eq!(ylong_json_add_item_to_object(null, str, item), 0); + ylong_json_delete(null); + let _ = Box::from_raw(str); + ylong_json_delete(item); + } + } + + /// UT test for `ylong_json_replace_object_item_by_index`. + /// + /// # Title + /// ut_ylong_json_replace_object_item_by_index + /// + /// # Brief + /// 1. Calls `ylong_json_replace_object_item_by_index` to replace an item in the object by index. + /// 2. Checks if the test results are correct. + #[test] + fn ut_ylong_json_replace_object_item_by_index() { + unsafe { + // Null ptr + let object = null_mut(); + let str = str_to_c_char("Hello World"); + let item = ylong_json_create_null(); + assert_eq!( + ylong_json_replace_object_item_by_index(object, str, item), + 0 + ); + let _ = Box::from_raw(str); + ylong_json_delete(item); + + // Null ptr + let object = ylong_json_create_object(); + let str = null(); + let item = ylong_json_create_null(); + assert_eq!( + ylong_json_replace_object_item_by_index(object, str, item), + 0 + ); + ylong_json_delete(object); + ylong_json_delete(item); + + // Null ptr + let object = ylong_json_create_object(); + let str = str_to_c_char("Hello World"); + let item = null_mut(); + assert_eq!( + ylong_json_replace_object_item_by_index(object, str, item), + 0 + ); + ylong_json_delete(object); + let _ = Box::from_raw(str); + + let object = ylong_json_create_object(); + let str = str_to_c_char("Init"); + let item = ylong_json_create_null(); + assert_eq!(ylong_json_add_item_to_object(object, str, item), 1); + let result = ylong_json_print_unformatted(object); + let result = CString::from_raw(result).into_string().unwrap(); + assert_eq!(result, "{\"Init\":null}"); + let item = ylong_json_create_bool(1); + assert_eq!( + ylong_json_replace_object_item_by_index(object, str, item), + 1 + ); + let _ = Box::from_raw(str); + let result = ylong_json_print_unformatted(object); + let result = CString::from_raw(result).into_string().unwrap(); + assert_eq!(result, "{\"Init\":true}"); + ylong_json_delete(object); + + let null = ylong_json_create_null(); + let str = str_to_c_char("Hello World"); + let item = ylong_json_create_null(); + assert_eq!(ylong_json_replace_object_item_by_index(null, str, item), 0); + ylong_json_delete(null); + let _ = Box::from_raw(str); + ylong_json_delete(item); + } + } + + /// UT test for `ylong_json_remove_object_item_by_index`. + /// + /// # Title + /// ut_ylong_json_remove_object_item_by_index + /// + /// # Brief + /// 1. Calls `ylong_json_remove_object_item_by_index` to remove an item in the object by index. + /// 2. Checks if the test results are correct. + #[test] + fn ut_ylong_json_remove_object_item_by_index() { + unsafe { + // Null ptr + let object = null_mut(); + let str = str_to_c_char("Hello World"); + assert!(ylong_json_remove_object_item_by_index(object, str).is_null()); + let _ = Box::from_raw(str); + + // Null ptr + let object = ylong_json_create_object(); + let str = null(); + assert!(ylong_json_remove_object_item_by_index(object, str).is_null()); + ylong_json_delete(object); + + let object = ylong_json_create_object(); + let str = str_to_c_char("Init"); + let item = ylong_json_create_null(); + assert_eq!(ylong_json_add_item_to_object(object, str, item), 1); + let result = ylong_json_print_unformatted(object); + let result = CString::from_raw(result).into_string().unwrap(); + assert_eq!(result, "{\"Init\":null}"); + let item = ylong_json_remove_object_item_by_index(object, str); + assert!(!item.is_null()); + assert_eq!(ylong_json_is_null(item), 1); + let result = ylong_json_print_unformatted(object); + let result = CString::from_raw(result).into_string().unwrap(); + assert_eq!(result, "{}"); + ylong_json_delete(object); + let _ = Box::from_raw(str); + ylong_json_delete(item); + + let null = ylong_json_create_null(); + let str = str_to_c_char("Hello World"); + assert!(ylong_json_remove_object_item_by_index(null, str).is_null()); + ylong_json_delete(null); + let _ = Box::from_raw(str); + } + } + + /// UT test for `ylong_json_delete_object_by_index`. + /// + /// # Title + /// ut_ylong_json_delete_object_by_index + /// + /// # Brief + /// 1. Calls `ylong_json_delete_object_by_index` to delete an item in the object by index. + /// 2. Checks if the test results are correct. + #[test] + fn ut_ylong_json_delete_object_by_index() { + unsafe { + // Null ptr + let object = null_mut(); + let str = str_to_c_char("Hello World"); + ylong_json_delete_object_item_by_index(object, str); + let _ = Box::from_raw(str); + + // Null ptr + let object = ylong_json_create_object(); + let str = null(); + ylong_json_delete_object_item_by_index(object, str); + ylong_json_delete(object); + + let object = ylong_json_create_object(); + let str = str_to_c_char("Init"); + let item = ylong_json_create_null(); + assert_eq!(ylong_json_add_item_to_object(object, str, item), 1); + let result = ylong_json_print_unformatted(object); + let result = CString::from_raw(result).into_string().unwrap(); + assert_eq!(result, "{\"Init\":null}"); + ylong_json_delete_object_item_by_index(object, str); + let result = ylong_json_print_unformatted(object); + let result = CString::from_raw(result).into_string().unwrap(); + assert_eq!(result, "{}"); + ylong_json_delete(object); + let _ = Box::from_raw(str); + + let null = ylong_json_create_null(); + let str = str_to_c_char("Hello World"); + ylong_json_delete_object_item_by_index(null, str); + ylong_json_delete(null); + let _ = Box::from_raw(str); + } + } + + /// UT test for `ylong_json_get_all_object_items`. + /// + /// # Title + /// ut_ylong_json_get_all_object_items + /// + /// # Brief + /// 1. Calls `ylong_json_get_all_object_items` to get all items in the object. + /// 2. Checks if the test results are correct. + #[test] + fn ut_ylong_json_get_all_object_items() { + unsafe { + // Null ptr + let object = null_mut(); + let mut len = 1i32; + let keys = malloc(size_of::<*mut c_char>() * (len as usize)) as *mut *mut c_char; + let values = + malloc(size_of::<*mut YlongJson>() * (len as usize)) as *mut *mut YlongJson; + assert_eq!( + ylong_json_get_all_object_items(object, keys, values, &mut len as *mut c_int), + 0 + ); + free(keys as *mut c_void); + free(values as *mut c_void); + + // Null ptr + let object = ylong_json_create_object(); + let mut len = 1i32; + let keys = null_mut(); + let values = + malloc(size_of::<*mut YlongJson>() * (len as usize)) as *mut *mut YlongJson; + assert_eq!( + ylong_json_get_all_object_items(object, keys, values, &mut len as *mut c_int), + 0 + ); + ylong_json_delete(object); + free(values as *mut c_void); + + // Null ptr + let object = ylong_json_create_object(); + let mut len = 1i32; + let keys = malloc(size_of::<*mut c_char>() * (len as usize)) as *mut *mut c_char; + let values = null_mut(); + assert_eq!( + ylong_json_get_all_object_items(object, keys, values, &mut len as *mut c_int), + 0 + ); + ylong_json_delete(object); + free(keys as *mut c_void); + + // Null ptr + let object = ylong_json_create_object(); + let len = 1i32; + let keys = malloc(size_of::<*mut c_char>() * (len as usize)) as *mut *mut c_char; + let values = + malloc(size_of::<*mut YlongJson>() * (len as usize)) as *mut *mut YlongJson; + let len = null_mut(); + assert_eq!( + ylong_json_get_all_object_items(object, keys, values, len), + 0 + ); + ylong_json_delete(object); + free(keys as *mut c_void); + free(values as *mut c_void); + + const TEXT: &str = r#"{"A":null,"B":1.0,"C":true,"D":"Test"}"#; + let text = str_to_c_char(TEXT); + let mut err_msg = null_mut(); + let object = ylong_json_parse(text, &mut err_msg as *mut *mut c_char); + let _ = Box::from_raw(text); + let mut len = 0i32; + assert_eq!( + ylong_json_get_object_size(object, &mut len as *mut c_int), + 1 + ); + assert_eq!(len, 4); + let keys = malloc(size_of::<*mut c_char>() * (len as usize)) as *mut *mut c_char; + let values = + malloc(size_of::<*mut YlongJson>() * (len as usize)) as *mut *mut YlongJson; + assert_eq!( + ylong_json_get_all_object_items(object, keys, values, &mut len as *mut c_int), + 1 + ); + let mut cnt = 0; + let key_result = ["A", "B", "C", "D"]; + let value_result = ["null", "1.0", "true", "\"Test\""]; + while cnt != len { + let key = *(keys.offset(cnt as isize)); + let key_str = CStr::from_ptr(key).to_str().unwrap(); + assert_eq!(key_str, key_result[cnt as usize]); + ylong_json_free_string(key); + + let item = *(values.offset(cnt as isize)); + let value = ylong_json_print_unformatted(item); + let value_str = CString::from_raw(value).into_string().unwrap(); + assert_eq!(value_str, value_result[cnt as usize]); + cnt += 1; + } + free(keys as *mut c_void); + free(values as *mut c_void); + ylong_json_delete(object); + + // 非 object + let null = ylong_json_create_null(); + let mut len = 1i32; + let keys = malloc(size_of::<*mut c_char>() * (len as usize)) as *mut *mut c_char; + let values = + malloc(size_of::<*mut YlongJson>() * (len as usize)) as *mut *mut YlongJson; + assert_eq!( + ylong_json_get_all_object_items(null, keys, values, &mut len as *mut c_int), + 0 + ); + ylong_json_delete(null); + free(keys as *mut c_void); + free(values as *mut c_void); + } + } + + /// UT test for `ylong_json_for_each_object_item`. + /// + /// # Title + /// ut_ylong_json_for_each_object_item + /// + /// # Brief + /// 1. Calls `ylong_json_for_each_object_item` to do `func` for each item in the object. + /// 2. Checks if the test results are correct. + #[test] + fn ut_ylong_json_for_each_object_item() { + unsafe { + unsafe extern "C" fn func(target: *mut YlongJson) { + ylong_json_set_int_value_to_number(target, 1000); + } + + // Null ptr + let object = null_mut(); + assert_eq!(ylong_json_for_each_object_item(object, func), 0); + + const TEXT: &str = r#"{"A":1,"B":2,"C":3,"D":null,"E":1.0}"#; + let text = str_to_c_char(TEXT); + let err_msg = null_mut(); + let object = ylong_json_parse(text, err_msg); + let _ = Box::from_raw(text); + let result = ylong_json_print_unformatted(object); + let result = CString::from_raw(result).into_string().unwrap(); + assert_eq!(result, "{\"A\":1,\"B\":2,\"C\":3,\"D\":null,\"E\":1.0}"); + assert_eq!(ylong_json_for_each_object_item(object, func), 1); + let result = ylong_json_print_unformatted(object); + let result = CString::from_raw(result).into_string().unwrap(); + assert_eq!( + result, + "{\"A\":1000,\"B\":1000,\"C\":1000,\"D\":null,\"E\":1000}" + ); + ylong_json_delete(object); + + let null = ylong_json_create_null(); + assert_eq!(ylong_json_for_each_object_item(null, func), 0); + ylong_json_delete(null); + } + } + + /// UT test for `ylong_json_get_object_node`. + /// + /// # Title + /// ut_ylong_json_get_object_node + /// + /// # Brief + /// 1. Calls `ylong_json_get_object_node` to get an object node. + /// 2. Checks if the test results are correct. + #[cfg(feature = "list_object")] + #[test] + fn ut_ylong_json_get_object_node() { + unsafe { + // Null ptr + let object = null_mut(); + let str = str_to_c_char("Hello World"); + let node = ylong_json_get_object_node(object, str); + assert!(node.is_null()); + let _ = Box::from_raw(str); + + // Null ptr + let object = ylong_json_create_object(); + let str = null_mut(); + let node = ylong_json_get_object_node(object, str); + assert!(node.is_null()); + ylong_json_delete(object); + + const TEXT: &str = r#"{"null":null}"#; + let text = str_to_c_char(TEXT); + let err_msg = null_mut(); + let object = ylong_json_parse(text, err_msg); + let _ = Box::from_raw(text); + let str = str_to_c_char("null"); + let node = ylong_json_get_object_node(object, str); + let _ = Box::from_raw(str); + assert!(!node.is_null()); + let item = ylong_json_get_item_from_object_node(node); + assert_eq!(ylong_json_is_null(item), 1); + ylong_json_delete(object); + + // Non-object + let null = ylong_json_create_null(); + let str = str_to_c_char("Hello World"); + let node = ylong_json_get_object_node(null, str); + let _ = Box::from_raw(str); + assert!(node.is_null()); + ylong_json_delete(null); + } + } + + /// UT test for `ylong_json_get_item_from_object_node`. + /// + /// # Title + /// ut_ylong_json_get_item_from_object_node + /// + /// # Brief + /// 1. Calls `ylong_json_get_item_from_object_node` to get the item of an object node. + /// 2. Checks if the test results are correct. + #[cfg(feature = "list_object")] + #[test] + fn ut_ylong_json_get_item_from_object_node() { + unsafe { + // Null ptr + let node = null_mut(); + let item = ylong_json_get_item_from_object_node(node); + assert!(item.is_null()); + + const TEXT: &str = r#"{"null":null}"#; + let text = str_to_c_char(TEXT); + let err_msg = null_mut(); + let object = ylong_json_parse(text, err_msg); + let str = str_to_c_char("null"); + let node = ylong_json_get_object_node(object, str); + assert!(!node.is_null()); + let item = ylong_json_get_item_from_object_node(node); + assert_eq!(ylong_json_is_null(item), 1); + let _ = Box::from_raw(text); + let _ = Box::from_raw(str); + ylong_json_delete(object); + } + } + + /// UT test for `ylong_json_add_item_to_object_then_get_node`. + /// + /// # Title + /// ut_ylong_json_add_item_to_object_then_get_node + /// + /// # Brief + /// 1. Calls `ylong_json_add_item_to_object_then_get_node` to add an item to the object and get the corresponding node. + /// 2. Checks if the test results are correct. + #[cfg(feature = "list_object")] + #[test] + fn ut_ylong_json_add_item_to_object_then_get_node() { + unsafe { + // Null ptr + let object = null_mut(); + let str = str_to_c_char("null"); + let item = ylong_json_create_null(); + let node = ylong_json_add_item_to_object_then_get_node(object, str, item); + assert!(node.is_null()); + let _ = Box::from_raw(str); + ylong_json_delete(item); + + // Null ptr + let object = ylong_json_create_object(); + let str = null_mut(); + let item = ylong_json_create_null(); + let node = ylong_json_add_item_to_object_then_get_node(object, str, item); + assert!(node.is_null()); + ylong_json_delete(object); + ylong_json_delete(item); + + // Null ptr + let object = ylong_json_create_object(); + let str = str_to_c_char("null"); + let item = null_mut(); + let node = ylong_json_add_item_to_object_then_get_node(object, str, item); + assert!(node.is_null()); + ylong_json_delete(object); + let _ = Box::from_raw(str); + + let object = ylong_json_create_object(); + let str = str_to_c_char("null"); + let item = ylong_json_create_null(); + let node = ylong_json_add_item_to_object_then_get_node(object, str, item); + assert!(!node.is_null()); + let item = ylong_json_get_item_from_object_node(node); + assert_eq!(ylong_json_is_null(item), 1); + ylong_json_delete(object); + let _ = Box::from_raw(str); + + let null = ylong_json_create_null(); + let str = str_to_c_char("null"); + let item = ylong_json_create_null(); + let node = ylong_json_add_item_to_object_then_get_node(null, str, item); + assert!(node.is_null()); + ylong_json_delete(null); + let _ = Box::from_raw(str); + ylong_json_delete(item); + } + } + + /// UT test for `ylong_json_replace_item_of_object_node`. + /// + /// # Title + /// ut_ylong_json_replace_item_of_object_node + /// + /// # Brief + /// 1. Calls `ylong_json_replace_item_of_object_node` to replace the item of an object node. + /// 2. Checks if the test results are correct. + #[cfg(feature = "list_object")] + #[test] + fn ut_ylong_json_replace_item_of_object_node() { + unsafe { + // Null ptr + let node = null_mut(); + let item = ylong_json_create_null(); + assert_eq!(ylong_json_replace_item_of_object_node(node, item), 0); + ylong_json_delete(item); + + // Null ptr + let object = ylong_json_create_object(); + let str = str_to_c_char("null"); + let item = ylong_json_create_null(); + let node = ylong_json_add_item_to_object_then_get_node(object, str, item); + let item = null_mut(); + assert_eq!(ylong_json_replace_item_of_object_node(node, item), 0); + ylong_json_delete(object); + let _ = Box::from_raw(str); + + let object = ylong_json_create_object(); + let str = str_to_c_char("null"); + let item = ylong_json_create_null(); + let node = ylong_json_add_item_to_object_then_get_node(object, str, item); + let result = ylong_json_print_unformatted(object); + let result = CString::from_raw(result).into_string().unwrap(); + assert_eq!(result, "{\"null\":null}"); + let item = ylong_json_create_bool(1); + assert_eq!(ylong_json_replace_item_of_object_node(node, item), 1); + let result = ylong_json_print_unformatted(object); + let result = CString::from_raw(result).into_string().unwrap(); + assert_eq!(result, "{\"null\":true}"); + ylong_json_delete(object); + let _ = Box::from_raw(str); + } + } + + /// UT test for `ylong_json_remove_object_node`. + /// + /// # Title + /// ut_ylong_json_remove_object_node + /// + /// # Brief + /// 1. Calls `ylong_json_remove_object_node` to remove an object node. + /// 2. Checks if the test results are correct. + #[cfg(feature = "list_object")] + #[test] + fn ut_ylong_json_remove_object_node() { + unsafe { + // Null ptr + let node = null_mut(); + let item = ylong_json_remove_object_node(node); + assert!(item.is_null()); + + let object = ylong_json_create_object(); + let str = str_to_c_char("null"); + let item = ylong_json_create_null(); + let node = ylong_json_add_item_to_object_then_get_node(object, str, item); + let _ = Box::from_raw(str); + let result = ylong_json_print_unformatted(object); + let result = CString::from_raw(result).into_string().unwrap(); + assert_eq!(result, "{\"null\":null}"); + let item = ylong_json_remove_object_node(node); + assert_eq!(ylong_json_is_null(item), 1); + let result = ylong_json_print_unformatted(object); + let result = CString::from_raw(result).into_string().unwrap(); + assert_eq!(result, "{}"); + ylong_json_delete(item); + ylong_json_delete(object); + } + } + + /// UT test for `ylong_json_delete_object_node`. + /// + /// # Title + /// ut_ylong_json_delete_object_node + /// + /// # Brief + /// 1. Calls `ylong_json_delete_object_node` to delete an object node. + /// 2. Checks if the test results are correct. + #[cfg(feature = "list_object")] + #[test] + fn ut_ylong_json_delete_object_node() { + unsafe { + // Null ptr scene, the process is correct if it exits without exception. + let node = null_mut(); + ylong_json_delete_object_node(node); + + let object = ylong_json_create_object(); + let str = str_to_c_char("null"); + let item = ylong_json_create_null(); + let node = ylong_json_add_item_to_object_then_get_node(object, str, item); + let result = ylong_json_print_unformatted(object); + let result = CString::from_raw(result).into_string().unwrap(); + assert_eq!(result, "{\"null\":null}"); + ylong_json_delete_object_node(node); + let result = ylong_json_print_unformatted(object); + let result = CString::from_raw(result).into_string().unwrap(); + assert_eq!(result, "{}"); + let _ = Box::from_raw(str); + ylong_json_delete(object); + } + } +} diff --git a/src/consts.rs b/src/consts.rs new file mode 100644 index 0000000000000000000000000000000000000000..ba71ba8b6f87ccebf633d948426ab2433e689783 --- /dev/null +++ b/src/consts.rs @@ -0,0 +1,214 @@ +/* + * Copyright (c) 2023 Huawei Device Co., Ltd. + * Licensed 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. + */ + +#![allow(dead_code)] +pub(crate) const COLON: u8 = b':'; +pub(crate) const COMMA: u8 = b','; +pub(crate) const DECIMAL_POINT: u8 = b'.'; +pub(crate) const LEFT_CURLY_BRACKET: u8 = b'{'; +pub(crate) const LEFT_SQUARE_BRACKET: u8 = b'['; +pub(crate) const MINUS: u8 = b'-'; +pub(crate) const PLUS: u8 = b'+'; +pub(crate) const RIGHT_CURLY_BRACKET: u8 = b'}'; +pub(crate) const RIGHT_SQUARE_BRACKET: u8 = b']'; +pub(crate) const SPACE: u8 = b' '; + +pub(crate) const ZERO: u8 = b'0'; +pub(crate) const ONE: u8 = b'1'; +pub(crate) const NINE: u8 = b'9'; +pub(crate) const A_LOWER: u8 = b'a'; +pub(crate) const A_UPPER: u8 = b'A'; +pub(crate) const E_LOWER: u8 = b'e'; +pub(crate) const E_UPPER: u8 = b'E'; +pub(crate) const F_LOWER: u8 = b'f'; +pub(crate) const F_UPPER: u8 = b'F'; +pub(crate) const N_LOWER: u8 = b'n'; +pub(crate) const T_LOWER: u8 = b't'; + +pub(crate) const WHITE_SPACE_SET: [u8; 4] = + [SPACE, HT_UNICODE as u8, LF_UNICODE as u8, CR_UNICODE as u8]; + +pub(crate) const BS: u8 = b'b'; +pub(crate) const BS_UNICODE: char = '\u{0008}'; +pub(crate) const BS_UNICODE_U8: u8 = 0x08; +pub(crate) const HT: u8 = b't'; +pub(crate) const HT_UNICODE: char = '\u{0009}'; +pub(crate) const HT_UNICODE_U8: u8 = 0x09; +pub(crate) const FF: u8 = b'f'; +pub(crate) const FF_UNICODE: char = '\u{000c}'; +pub(crate) const FF_UNICODE_U8: u8 = 0x0c; +pub(crate) const CR: u8 = b'r'; +pub(crate) const CR_UNICODE: char = '\u{000d}'; +pub(crate) const CR_UNICODE_U8: u8 = 0x0d; +pub(crate) const LF: u8 = b'n'; +pub(crate) const LF_UNICODE: char = '\u{000a}'; +pub(crate) const LF_UNICODE_U8: u8 = 0x0a; +pub(crate) const UNICODE: u8 = b'u'; +pub(crate) const QUOTATION_MARK: u8 = b'\"'; +pub(crate) const REVERSE_SOLIDUS: u8 = b'\\'; +pub(crate) const SOLIDUS: u8 = b'/'; + +pub(crate) const JSON_REVERSE_SOLIDUS: &[u8] = b"\\\\"; +pub(crate) const JSON_QUOTATION_MARK: &[u8] = b"\\\""; +pub(crate) const JSON_BS: &[u8] = b"\\b"; +pub(crate) const JSON_FF: &[u8] = b"\\f"; +pub(crate) const JSON_LF: &[u8] = b"\\n"; +pub(crate) const JSON_CR: &[u8] = b"\\r"; +pub(crate) const JSON_HT: &[u8] = b"\\t"; + +pub(crate) const NULL_STR: &[u8] = b"null"; +pub(crate) const NULL_LEFT_STR: &[u8] = b"ull"; +pub(crate) const FALSE_STR: &[u8] = b"false"; +pub(crate) const FALSE_LEFT_STR: &[u8] = b"alse"; +pub(crate) const TRUE_STR: &[u8] = b"true"; +pub(crate) const TRUE_LEFT_STR: &[u8] = b"rue"; +pub(crate) const UNICODE_START_STR: &[u8] = b"\\u"; +pub(crate) const COLON_STR: &[u8] = b":"; +pub(crate) const COMMA_STR: &[u8] = b","; +pub(crate) const FOUR_SPACES_STR: &[u8] = b" "; +pub(crate) const LEFT_CURLY_BRACKET_STR: &[u8] = b"{"; +pub(crate) const LEFT_SQUARE_BRACKET_STR: &[u8] = b"["; +pub(crate) const LINE_FEED_STR: &[u8] = b"\n"; +pub(crate) const QUOTATION_MARK_STR: &[u8] = b"\""; +pub(crate) const RIGHT_CURLY_BRACKET_STR: &[u8] = b"}"; +pub(crate) const RIGHT_SQUARE_BRACKET_STR: &[u8] = b"]"; +pub(crate) const SPACE_STR: &[u8] = b" "; + +pub(crate) const RECURSION_LIMIT: u32 = 128; + +// Improves the string read rate by looking up tables. +pub(crate) static ESCAPE: [bool; 256] = { + const CT: bool = true; // Control character \x00..=\x1F + const QU: bool = true; // Quotation mark \x22 + const BS: bool = true; // Backslash \x5C + const __: bool = false; // Other character + [ + // 1 2 3 4 5 6 7 8 9 A B C D E F + CT, CT, CT, CT, CT, CT, CT, CT, CT, CT, CT, CT, CT, CT, CT, CT, // 0 + CT, CT, CT, CT, CT, CT, CT, CT, CT, CT, CT, CT, CT, CT, CT, CT, // 1 + __, __, QU, __, __, __, __, __, __, __, __, __, __, __, __, __, // 2 + __, __, __, __, __, __, __, __, __, __, __, __, __, __, __, __, // 3 + __, __, __, __, __, __, __, __, __, __, __, __, __, __, __, __, // 4 + __, __, __, __, __, __, __, __, __, __, __, __, BS, __, __, __, // 5 + __, __, __, __, __, __, __, __, __, __, __, __, __, __, __, __, // 6 + __, __, __, __, __, __, __, __, __, __, __, __, __, __, __, __, // 7 + __, __, __, __, __, __, __, __, __, __, __, __, __, __, __, __, // 8 + __, __, __, __, __, __, __, __, __, __, __, __, __, __, __, __, // 9 + __, __, __, __, __, __, __, __, __, __, __, __, __, __, __, __, // A + __, __, __, __, __, __, __, __, __, __, __, __, __, __, __, __, // B + __, __, __, __, __, __, __, __, __, __, __, __, __, __, __, __, // C + __, __, __, __, __, __, __, __, __, __, __, __, __, __, __, __, // D + __, __, __, __, __, __, __, __, __, __, __, __, __, __, __, __, // E + __, __, __, __, __, __, __, __, __, __, __, __, __, __, __, __, // F + ] +}; + +// TODO: Consider modifying the structure of PRINT_MAP. +#[cfg(not(feature = "ascii_only"))] +#[derive(Debug, Clone, Copy, PartialEq, Eq)] +pub(crate) enum PrintMapItem<'a> { + Other, + Control, + Special(&'a [u8]), +} + +// Improves the string output rate by looking up the table. +#[cfg(not(feature = "ascii_only"))] +pub(crate) static PRINT_MAP: [PrintMapItem; 256] = { + const BS: PrintMapItem = PrintMapItem::Special(b"\\b"); // BS 退格 \x08 + const HT: PrintMapItem = PrintMapItem::Special(b"\\t"); // HT 水平定位符 \x09 + const LF: PrintMapItem = PrintMapItem::Special(b"\\n"); // LF 换行 \x0A + const FF: PrintMapItem = PrintMapItem::Special(b"\\f"); // FF 换页 \x0C + const CR: PrintMapItem = PrintMapItem::Special(b"\\r"); // CR 归位 \x0D + const QU: PrintMapItem = PrintMapItem::Special(b"\\\""); // 双引号 \x22 + const SO: PrintMapItem = PrintMapItem::Special(b"/"); // 斜杠 \x2F + const RS: PrintMapItem = PrintMapItem::Special(b"\\\\"); // 反斜杠 \x5C + const CT: PrintMapItem = PrintMapItem::Control; // 控制字符 \x00..=\x1F + const __: PrintMapItem = PrintMapItem::Other; // 其他字符 + [ + // 1 2 3 4 5 6 7 8 9 A B C D E F + CT, CT, CT, CT, CT, CT, CT, CT, BS, HT, LF, CT, FF, CR, CT, CT, // 0 + CT, CT, CT, CT, CT, CT, CT, CT, CT, CT, CT, CT, CT, CT, CT, CT, // 1 + __, __, QU, __, __, __, __, __, __, __, __, __, __, __, __, SO, // 2 + __, __, __, __, __, __, __, __, __, __, __, __, __, __, __, __, // 3 + __, __, __, __, __, __, __, __, __, __, __, __, __, __, __, __, // 4 + __, __, __, __, __, __, __, __, __, __, __, __, RS, __, __, __, // 5 + __, __, __, __, __, __, __, __, __, __, __, __, __, __, __, __, // 6 + __, __, __, __, __, __, __, __, __, __, __, __, __, __, __, __, // 7 + __, __, __, __, __, __, __, __, __, __, __, __, __, __, __, __, // 8 + __, __, __, __, __, __, __, __, __, __, __, __, __, __, __, __, // 9 + __, __, __, __, __, __, __, __, __, __, __, __, __, __, __, __, // A + __, __, __, __, __, __, __, __, __, __, __, __, __, __, __, __, // B + __, __, __, __, __, __, __, __, __, __, __, __, __, __, __, __, // C + __, __, __, __, __, __, __, __, __, __, __, __, __, __, __, __, // D + __, __, __, __, __, __, __, __, __, __, __, __, __, __, __, __, // E + __, __, __, __, __, __, __, __, __, __, __, __, __, __, __, __, // F + ] +}; + +#[cfg(not(feature = "ascii_only"))] +#[cfg(test)] +mod ut_consts { + use crate::consts::PrintMapItem; + + /// UT test case for `PrintMapItem::clone`. + /// + /// # Title + /// ut_print_map_item_clone + /// + /// # Brief + /// 1. Creates a `PrintMapItem`. + /// 2. Calls `PrintMapItem::clone`. + /// 3. Checks if the results are correct. + #[allow(clippy::clone_on_copy)] + #[test] + fn ut_print_map_item_clone() { + let item = PrintMapItem::Other; + let item = item.clone(); + assert_eq!(item, PrintMapItem::Other); + + let item = PrintMapItem::Control; + let item = item.clone(); + assert_eq!(item, PrintMapItem::Control); + + let item = PrintMapItem::Special(b"abc"); + let item = item.clone(); + assert_eq!(item, PrintMapItem::Special(b"abc")); + } + + /// UT test case for `PrintMapItem::copy`. + /// + /// # Title + /// ut_print_map_item_copy + /// + /// # Brief + /// 1. Creates a `PrintMapItem`. + /// 2. Calls `PrintMapItem::copy`. + /// 3. Checks if the results are correct. + #[test] + fn ut_print_map_item_copy() { + let item1 = PrintMapItem::Other; + let _item2 = item1; + assert_eq!(item1, PrintMapItem::Other); + + let item1 = PrintMapItem::Control; + let _item2 = item1; + assert_eq!(item1, PrintMapItem::Control); + + let item1 = PrintMapItem::Special(b"abc"); + let _item2 = item1; + assert_eq!(item1, PrintMapItem::Special(b"abc")); + } +} diff --git a/src/deserializer.rs b/src/deserializer.rs new file mode 100644 index 0000000000000000000000000000000000000000..43de3571aa72da0fcf400c34d44c7a876128f7dd --- /dev/null +++ b/src/deserializer.rs @@ -0,0 +1,1099 @@ +/* + * Copyright (c) 2023 Huawei Device Co., Ltd. + * Licensed 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. + */ + +use crate::reader::{BytesReader, Cacheable, IoReader, SliceReader}; +use serde::de; +use serde::de::{ + DeserializeOwned, DeserializeSeed, EnumAccess, IntoDeserializer, MapAccess, SeqAccess, + VariantAccess, Visitor, +}; +use serde::Deserialize; +use std::io::Read; + +use crate::{consts::*, error::*, states::*, Number, ParseError::*}; + +#[cfg(feature = "c_adapter")] +type JsonString = CString; +#[cfg(not(feature = "c_adapter"))] +type JsonString = String; + +impl Number { + fn visit<'de, V>(self, visitor: V) -> Result + where + V: Visitor<'de>, + { + match self { + Number::Unsigned(x) => visitor.visit_u64(x), + Number::Signed(x) => visitor.visit_i64(x), + Number::Float(x) => visitor.visit_f64(x), + } + } +} + +/// A struct that can deserialize JSON into Rust values of user's types. +pub(crate) struct Deserializer +where + R: BytesReader + Cacheable, +{ + pub(crate) reader: R, + pub(crate) recursion_depth: u32, +} + +impl Deserializer +where + R: BytesReader + Cacheable, +{ + /// Creates a new instance of Deserializer. + /// This method is usually used in the following methods: + /// - Deserializer::new_from_reader + /// - Deserializer::new_from_slice + pub fn new(reader: R) -> Self { + Deserializer { + reader, + recursion_depth: 0, + } + } +} + +/// Creates an instance of Deserializer from reader. +impl Deserializer> { + pub fn new_from_io(reader: R) -> Self { + Deserializer::new(IoReader::new(reader)) + } +} + +/// Creates an instance of Deserializer from slice. +impl<'a> Deserializer> { + pub fn new_from_slice(slice: &'a [u8]) -> Self { + Deserializer::new(SliceReader::new(slice)) + } +} + +/// Deserializes an instance of type `T` from an IO stream of JSON. +/// # Example +/// ```not run +/// use serde::Deserialize; +/// use std::fs::File; +/// use ylong_json::from_reader; +/// +/// #[derive(Deserialize, PartialEq, Debug)] +/// struct Test { +/// int: u32, +/// seq: Vec, +/// tup: (i32, i32, i32), +/// } +/// +/// let expected = Test { +/// int: 1, +/// seq: vec![String::from("abcd"), String::from("efgh")], +/// tup: (1, 2, 3), +/// }; +/// let file = File::open("./test.txt").unwrap(); +/// assert_eq!(expected, from_reader(file).unwrap()); +/// ``` +pub fn from_reader(reader: R) -> Result +where + R: Read, + T: DeserializeOwned, +{ + let mut deserializer = Deserializer::new_from_io(reader); + let t = T::deserialize(&mut deserializer)?; + match deserializer.reader.peek() { + Ok(None) => Ok(t), + _ => Err(Error::Parsing(ParsingUnfinished)), + } +} + +/// Deserializes an instance of type `T` from bytes. +/// # Example +/// ``` +/// use serde::Deserialize; +/// use ylong_json::from_slice; +/// +/// #[derive(Deserialize, PartialEq, Debug)] +/// struct Test { +/// int: u32, +/// seq: Vec, +/// tup: (i32, i32, i32), +/// } +/// +/// let slice = r#"{"int":1,"seq":["abcd","efgh"],"tup":[1,2,3]}"#.as_bytes(); +/// let expected = Test { +/// int: 1, +/// seq: vec![String::from("abcd"), String::from("efgh")], +/// tup: (1, 2, 3), +/// }; +/// assert_eq!(expected, from_slice(slice).unwrap()) +/// ``` +pub fn from_slice<'a, T>(slice: &'a [u8]) -> Result +where + T: Deserialize<'a>, +{ + let mut deserializer = Deserializer::new_from_slice(slice); + let t = T::deserialize(&mut deserializer)?; + match deserializer.reader.peek() { + Ok(None) => Ok(t), + _ => Err(Error::Parsing(ParsingUnfinished)), + } +} + +/// Deserializes an instance of type `T` from str. +/// # Example +/// ``` +/// use serde::Deserialize; +/// use ylong_json::from_str; +/// +/// #[derive(Deserialize, PartialEq, Debug)] +/// struct Test { +/// int: u32, +/// seq: Vec, +/// tup: (i32, i32, i32), +/// } +/// +/// let str = r#"{"int":1,"seq":["abcd","efgh"],"tup":[1,2,3]}"#; +/// let expected = Test { +/// int: 1, +/// seq: vec![String::from("abcd"), String::from("efgh")], +/// tup: (1, 2, 3), +/// }; +/// assert_eq!(expected, from_str(str).unwrap()) +/// ``` +pub fn from_str<'a, T>(str: &'a str) -> Result +where + T: Deserialize<'a>, +{ + from_slice(str.as_bytes()) +} + +impl Deserializer +where + R: BytesReader + Cacheable, +{ + // Look at the next character without moving cursor. + fn peek_char(&mut self) -> Result, Error> { + self.reader.peek().map_err(Error::new_reader) + } + + // Get the next character and move the cursor to the next place. + fn next_char(&mut self) -> Result, Error> { + self.reader.next().map_err(Error::new_reader) + } + + // Discard the next character and move the cursor to the next place. + fn discard_char(&mut self) { + self.reader.discard(); + } + + // Parse value of bool, `true` or `false`. + fn parse_bool(&mut self) -> Result { + let peek_ch = match eat_whitespace_until_not!(self) { + Some(ch) => ch, + None => { + return Err(Error::Parsing(ParsingUnfinished)); + } + }; + + match peek_ch { + b't' => { + self.reader.discard(); + match_str!(self, b"rue"); + Ok(true) + } + b'f' => { + self.reader.discard(); + match_str!(self, b"alse"); + Ok(false) + } + _ => { + unexpected_character!(self) + } + } + } + + fn de_parse_number<'de, V>(&mut self, visitor: V) -> Result + where + V: Visitor<'de>, + { + let peek_ch = match eat_whitespace_until_not!(self) { + Some(ch) => ch, + None => { + return Err(Error::Parsing(ParsingUnfinished)); + } + }; + + match peek_ch { + b'-' => parse_number(self)?.visit(visitor), + b'0'..=b'9' => parse_number(self)?.visit(visitor), + _ => unexpected_character!(self), + } + } + + fn de_parse_string(&mut self) -> Result { + match self.peek_char()? { + Some(b'"') => self.discard_char(), + _ => return unexpected_character!(self), + } + parse_string(self) + } +} + +impl<'de, 'a, R> de::Deserializer<'de> for &'a mut Deserializer +where + R: BytesReader + Cacheable, +{ + type Error = Error; + + // Choose a parsing method to parse value based on the input data. + fn deserialize_any(self, visitor: V) -> Result + where + V: Visitor<'de>, + { + let peek_ch = match eat_whitespace_until_not!(self) { + Some(ch) => ch, + None => { + return Err(Error::Parsing(ParsingUnfinished)); + } + }; + + match peek_ch { + b'n' => self.deserialize_unit(visitor), + b't' | b'f' => self.deserialize_bool(visitor), + b'"' => self.deserialize_str(visitor), + b'0'..=b'9' => self.deserialize_u64(visitor), + b'-' => self.deserialize_i64(visitor), + b'[' => self.deserialize_seq(visitor), + b'{' => self.deserialize_map(visitor), + _ => unexpected_character!(self), + } + } + + fn deserialize_bool(self, visitor: V) -> Result + where + V: Visitor<'de>, + { + visitor.visit_bool(self.parse_bool()?) + } + + fn deserialize_i8(self, visitor: V) -> Result + where + V: Visitor<'de>, + { + self.de_parse_number(visitor) + } + + fn deserialize_i16(self, visitor: V) -> Result + where + V: Visitor<'de>, + { + self.de_parse_number(visitor) + } + + fn deserialize_i32(self, visitor: V) -> Result + where + V: Visitor<'de>, + { + self.de_parse_number(visitor) + } + + fn deserialize_i64(self, visitor: V) -> Result + where + V: Visitor<'de>, + { + self.de_parse_number(visitor) + } + + fn deserialize_u8(self, visitor: V) -> Result + where + V: Visitor<'de>, + { + self.de_parse_number(visitor) + } + + fn deserialize_u16(self, visitor: V) -> Result + where + V: Visitor<'de>, + { + self.de_parse_number(visitor) + } + + fn deserialize_u32(self, visitor: V) -> Result + where + V: Visitor<'de>, + { + self.de_parse_number(visitor) + } + + fn deserialize_u64(self, visitor: V) -> Result + where + V: Visitor<'de>, + { + self.de_parse_number(visitor) + } + + fn deserialize_f32(self, visitor: V) -> Result + where + V: Visitor<'de>, + { + self.de_parse_number(visitor) + } + + fn deserialize_f64(self, visitor: V) -> Result + where + V: Visitor<'de>, + { + self.de_parse_number(visitor) + } + + fn deserialize_char(self, visitor: V) -> Result + where + V: Visitor<'de>, + { + self.deserialize_str(visitor) + } + + fn deserialize_str(self, visitor: V) -> Result + where + V: Visitor<'de>, + { + let str = self.de_parse_string()?; + #[cfg(feature = "c_adapter")] + return visitor.visit_str(str.to_str()?); + + #[cfg(not(feature = "c_adapter"))] + visitor.visit_str(str.as_str()) + } + + fn deserialize_string(self, visitor: V) -> Result + where + V: Visitor<'de>, + { + self.deserialize_str(visitor) + } + + fn deserialize_bytes(self, visitor: V) -> Result + where + V: Visitor<'de>, + { + let peek_ch = match eat_whitespace_until_not!(self) { + Some(ch) => ch, + None => { + return Err(Error::Parsing(ParsingUnfinished)); + } + }; + + match peek_ch { + b'"' => { + let v = parse_string_inner(self)?; + visitor.visit_bytes(&v) + } + b'[' => self.deserialize_seq(visitor), + _ => unexpected_character!(self), + } + } + + fn deserialize_byte_buf(self, visitor: V) -> Result + where + V: Visitor<'de>, + { + self.deserialize_bytes(visitor) + } + + fn deserialize_option(self, visitor: V) -> Result + where + V: Visitor<'de>, + { + let peek_ch = match eat_whitespace_until_not!(self) { + Some(ch) => ch, + None => { + println!("arrive optin None branch"); + return Err(Error::Parsing(ParsingUnfinished)); + } + }; + + match peek_ch { + b'n' => { + self.discard_char(); + match_str!(self, b"ull"); + visitor.visit_none() + } + _ => visitor.visit_some(self), + } + } + + fn deserialize_unit(self, visitor: V) -> Result + where + V: Visitor<'de>, + { + let peek_ch = match eat_whitespace_until_not!(self) { + Some(ch) => ch, + None => { + return Err(Error::Parsing(ParsingUnfinished)); + } + }; + + match peek_ch { + b'n' => { + self.discard_char(); + match_str!(self, b"ull"); + visitor.visit_unit() + } + _ => unexpected_character!(self), + } + } + + fn deserialize_unit_struct( + self, + _name: &'static str, + visitor: V, + ) -> Result + where + V: Visitor<'de>, + { + self.deserialize_unit(visitor) + } + + fn deserialize_newtype_struct( + self, + _name: &'static str, + visitor: V, + ) -> Result + where + V: Visitor<'de>, + { + visitor.visit_newtype_struct(self) + } + + fn deserialize_seq(self, visitor: V) -> Result + where + V: Visitor<'de>, + { + let peek_ch = match eat_whitespace_until_not!(self) { + Some(ch) => ch, + None => { + return Err(Error::Parsing(ParsingUnfinished)); + } + }; + + match peek_ch { + b'[' => { + self.discard_char(); + let value = visitor.visit_seq(SeqAssistant::new(self))?; + + let peek_ch_inner = match eat_whitespace_until_not!(self) { + Some(ch) => ch, + None => { + return Err(Error::Parsing(ParsingUnfinished)); + } + }; + + match peek_ch_inner { + b']' => { + self.discard_char(); + Ok(value) + } + _ => unexpected_character!(self), + } + } + _ => unexpected_character!(self), + } + } + + fn deserialize_tuple(self, _len: usize, visitor: V) -> Result + where + V: Visitor<'de>, + { + self.deserialize_seq(visitor) + } + + fn deserialize_tuple_struct( + self, + _name: &'static str, + _len: usize, + visitor: V, + ) -> Result + where + V: Visitor<'de>, + { + self.deserialize_seq(visitor) + } + + fn deserialize_map(self, visitor: V) -> Result + where + V: Visitor<'de>, + { + let peek_ch = match eat_whitespace_until_not!(self) { + Some(ch) => ch, + None => { + return Err(Error::Parsing(ParsingUnfinished)); + } + }; + + match peek_ch { + b'{' => { + self.discard_char(); + let value = visitor.visit_map(SeqAssistant::new(self))?; + + let peek_ch_inner = match eat_whitespace_until_not!(self) { + Some(ch) => ch, + None => { + return Err(Error::Parsing(ParsingUnfinished)); + } + }; + + match peek_ch_inner { + b'}' => { + self.discard_char(); + Ok(value) + } + _ => unexpected_character!(self), + } + } + _ => unexpected_character!(self), + } + } + + fn deserialize_struct( + self, + _name: &'static str, + _fields: &'static [&'static str], + visitor: V, + ) -> Result + where + V: Visitor<'de>, + { + self.deserialize_map(visitor) + } + + fn deserialize_enum( + self, + _name: &'static str, + _variants: &'static [&'static str], + visitor: V, + ) -> Result + where + V: Visitor<'de>, + { + let peek_ch = match eat_whitespace_until_not!(self) { + Some(ch) => ch, + None => { + return Err(Error::Parsing(ParsingUnfinished)); + } + }; + + match peek_ch { + b'"' => { + #[cfg(feature = "c_adapter")] + return visitor + .visit_enum(self.de_parse_string()?.into_string()?.into_deserializer()); + + #[cfg(not(feature = "c_adapter"))] + visitor.visit_enum(self.de_parse_string()?.into_deserializer()) + } + _ => { + if self.next_char()? == Some(b'{') { + let value = visitor.visit_enum(EnumAssistant::new(self))?; + + if self.next_char()? == Some(b'}') { + Ok(value) + } else { + unexpected_character!(self) + } + } else { + unexpected_character!(self) + } + } + } + } + + fn deserialize_identifier(self, visitor: V) -> Result + where + V: Visitor<'de>, + { + self.deserialize_str(visitor) + } + + fn deserialize_ignored_any(self, visitor: V) -> Result + where + V: Visitor<'de>, + { + self.deserialize_any(visitor) + } +} + +struct SeqAssistant<'a, R: 'a> +where + R: BytesReader + Cacheable, +{ + deserializer: &'a mut Deserializer, + is_first: bool, +} + +impl<'a, R: 'a> SeqAssistant<'a, R> +where + R: BytesReader + Cacheable, +{ + fn new(deserializer: &'a mut Deserializer) -> Self { + SeqAssistant { + deserializer, + is_first: true, + } + } +} + +impl<'de, 'a, R> SeqAccess<'de> for SeqAssistant<'a, R> +where + R: BytesReader + Cacheable, +{ + type Error = Error; + + fn next_element_seed(&mut self, seed: T) -> Result, Error> + where + T: DeserializeSeed<'de>, + { + let peek_ch = match eat_whitespace_until_not!(self.deserializer) { + Some(ch) => ch, + None => { + return Err(Error::Parsing(ParsingUnfinished)); + } + }; + + match peek_ch { + b']' => Ok(None), + b',' if self.is_first => { + unexpected_character!(self.deserializer) + } + b',' => { + self.deserializer.discard_char(); + Ok(Some(seed.deserialize(&mut *self.deserializer)?)) + } + _ => { + self.is_first = false; + Ok(Some(seed.deserialize(&mut *self.deserializer)?)) + } + } + } +} + +impl<'de, 'a, R> MapAccess<'de> for SeqAssistant<'a, R> +where + R: BytesReader + Cacheable, +{ + type Error = Error; + fn next_key_seed(&mut self, seed: K) -> Result, Error> + where + K: DeserializeSeed<'de>, + { + let peek_ch = match eat_whitespace_until_not!(self.deserializer) { + Some(ch) => ch, + None => { + return Err(Error::Parsing(ParsingUnfinished)); + } + }; + + match peek_ch { + b'}' => Ok(None), + b',' if self.is_first => { + unexpected_character!(self.deserializer) + } + b',' => { + self.deserializer.discard_char(); + Ok(Some(seed.deserialize(&mut *self.deserializer)?)) + } + _ => { + self.is_first = false; + Ok(Some(seed.deserialize(&mut *self.deserializer)?)) + } + } + } + + fn next_value_seed(&mut self, seed: V) -> Result + where + V: DeserializeSeed<'de>, + { + match eat_whitespace_until_not!(self.deserializer) { + Some(b':') => { + self.deserializer.discard_char(); + seed.deserialize(&mut *self.deserializer) + } + Some(_ch) => unexpected_character!(self.deserializer), + None => Err(Error::Parsing(ParsingUnfinished)), + } + } +} + +struct EnumAssistant<'a, R: 'a> +where + R: BytesReader + Cacheable, +{ + deserializer: &'a mut Deserializer, +} + +impl<'a, R: 'a> EnumAssistant<'a, R> +where + R: BytesReader + Cacheable, +{ + fn new(deserializer: &'a mut Deserializer) -> Self { + EnumAssistant { deserializer } + } +} + +impl<'de, 'a, R: 'a> EnumAccess<'de> for EnumAssistant<'a, R> +where + R: BytesReader + Cacheable, +{ + type Error = Error; + type Variant = Self; + + fn variant_seed(self, seed: V) -> Result<(V::Value, Self::Variant), Self::Error> + where + V: DeserializeSeed<'de>, + { + let value = seed.deserialize(&mut *self.deserializer)?; + + match eat_whitespace_until_not!(self.deserializer) { + Some(b':') => { + self.deserializer.discard_char(); + Ok((value, self)) + } + _ => unexpected_character!(self.deserializer), + } + } +} + +impl<'de, 'a, R: 'a> VariantAccess<'de> for EnumAssistant<'a, R> +where + R: BytesReader + Cacheable, +{ + type Error = Error; + + fn unit_variant(self) -> Result<(), Error> { + serde::de::Deserialize::deserialize(self.deserializer) + } + + fn newtype_variant_seed(self, seed: T) -> Result + where + T: DeserializeSeed<'de>, + { + seed.deserialize(self.deserializer) + } + + fn tuple_variant(self, _len: usize, visitor: V) -> Result + where + V: Visitor<'de>, + { + serde::de::Deserializer::deserialize_seq(self.deserializer, visitor) + } + + fn struct_variant( + self, + _fields: &'static [&'static str], + visitor: V, + ) -> Result + where + V: Visitor<'de>, + { + serde::de::Deserializer::deserialize_map(self.deserializer, visitor) + } +} + +#[cfg(test)] +mod ut_test_for_deserializer { + use crate::deserializer::{from_slice, from_str}; + use serde::Deserialize; + use std::borrow::Cow; + use std::collections::HashMap; + use std::option::Option; + + /// UT test to deserialize simple types + /// + /// # Title + /// ut_deserialize_simple + /// + /// # Brief + /// 1.Uses deserializer::from_slice method to deserialize simple types. + /// 2.Checks if the test results are correct. + #[test] + fn ut_deserialize_simple() { + let slice_null = b"null"; + let expected: Option = None; + assert_eq!(expected, from_slice(slice_null).unwrap()); + + let slice_bool = b"true"; + let expected = true; + assert_eq!(expected, from_slice(slice_bool).unwrap()); + + let slice_bool = b"false"; + let expected = false; + assert_eq!(expected, from_slice(slice_bool).unwrap()); + + let slice_num = b"123"; + let expected: u8 = 123; + assert_eq!(expected, from_slice(slice_num).unwrap()); + + let slice_num = b"123"; + let expected: u16 = 123; + assert_eq!(expected, from_slice(slice_num).unwrap()); + + let slice_num = b"123"; + let expected: u32 = 123; + assert_eq!(expected, from_slice(slice_num).unwrap()); + + let slice_num = b"-12"; + let expected: i8 = -12; + assert_eq!(expected, from_slice(slice_num).unwrap()); + + let slice_num = b"-12"; + let expected: i16 = -12; + assert_eq!(expected, from_slice(slice_num).unwrap()); + + let slice_num = b"-321"; + let expected: i32 = -321; + assert_eq!(expected, from_slice(slice_num).unwrap()); + + let slice_num = b"-321.123"; + let expected: f32 = -321.123; + assert_eq!(expected, from_slice(slice_num).unwrap()); + + let slice_char = b"\"c\""; + let expected = 'c'; + assert_eq!(expected, from_slice::(slice_char).unwrap()); + + let slice_str = b"\"string\""; + let expected = String::from("string"); + assert_eq!(expected, from_slice::(slice_str).unwrap()); + + let slice_option = b"true"; + let expected = Some(true); + assert_eq!(expected, from_slice::>(slice_option).unwrap()); + + let slice_option = b"null"; + let expected = None; + assert_eq!(expected, from_slice::>(slice_option).unwrap()); + + let slice_seq = b"[1, 2, 3]"; + let expected: Vec = vec![1, 2, 3]; + assert_eq!(expected, from_slice::>(slice_seq).unwrap()); + + let slice_map = r#"{ true : 1 }"#.as_bytes(); + let mut expected = HashMap::new(); + expected.insert(true, 1); + assert_eq!( + expected, + from_slice::>(slice_map).unwrap() + ); + } + + /// UT test to deserialize simple types with abnormal input of JSON. + /// + /// # Title + /// ut_deserialize_simple_error + /// + /// # Brief + /// 1.Uses deserializer::from_slice method to deserialize simple types with abnormal input of JSON. + /// 2.Checks if the test results are correct. + #[test] + fn ut_deserialize_simple_error() { + // The following is the test for abnormal input of JSON. + let incorrect_input = b"nul"; + let res = from_slice::(incorrect_input); + assert!(res.is_err()); + + let incorrect_input = b" "; + let res = from_slice::(incorrect_input); + assert!(res.is_err()); + + let incorrect_input = b"tru"; + let res = from_slice::(incorrect_input); + assert!(res.is_err()); + + let incorrect_input = b"fals"; + let res = from_slice::(incorrect_input); + assert!(res.is_err()); + + let incorrect_input = b"ruet"; + let res = from_slice::(incorrect_input); + assert!(res.is_err()); + + let incorrect_input = b" "; + let res = from_slice::(incorrect_input); + assert!(res.is_err()); + + let incorrect_input = b"12x"; + let res = from_slice::(incorrect_input); + assert!(res.is_err()); + + let incorrect_input = b"-12x"; + let res = from_slice::(incorrect_input); + assert!(res.is_err()); + + let incorrect_input = b"-12.21x"; + let res = from_slice::(incorrect_input); + assert!(res.is_err()); + + let incorrect_input = b" "; + let res = from_slice::(incorrect_input); + assert!(res.is_err()); + + let incorrect_input = b"string"; + let res = from_slice::(incorrect_input); + assert!(res.is_err()); + + let incorrect_input = b"\"string"; + let res = from_slice::(incorrect_input); + assert!(res.is_err()); + + let incorrect_input = b" "; + let res = from_slice::>(incorrect_input); + assert!(res.is_err()); + + let incorrect_input = b" "; + let res = from_slice::>(incorrect_input); + assert!(res.is_err()); + + let incorrect_input = b" ["; + let res = from_slice::>(incorrect_input); + assert!(res.is_err()); + + let incorrect_input = b" [ 1"; + let res = from_slice::>(incorrect_input); + assert!(res.is_err()); + + let incorrect_input = r#" "#.as_bytes(); + let res = from_slice::>(incorrect_input); + assert!(res.is_err()); + + let incorrect_input = r#"{ true x: 1 }"#.as_bytes(); + let res = from_slice::>(incorrect_input); + assert!(res.is_err()); + + let incorrect_input = r#"{ : 1 }"#.as_bytes(); + let res = from_slice::>(incorrect_input); + assert!(res.is_err()); + } + + /// UT test to deserialize struct + /// + /// # Title + /// ut_deserialize_struct + /// + /// # Brief + /// 1.Uses deserializer::from_str method to deserialize struct. + /// 2.Checks if the test results are correct. + #[test] + fn ut_deserialize_struct() { + #[derive(Deserialize, PartialEq, Debug)] + struct TestUnit; + let str = "null"; + let expected = TestUnit; + assert_eq!(expected, from_str(str).unwrap()); + + #[derive(Deserialize, PartialEq, Debug)] + struct TestNewtype(u32); + let str = "123"; + let expected = TestNewtype(123); + assert_eq!(expected, from_str(str).unwrap()); + + #[derive(Deserialize, PartialEq, Debug)] + struct TestTuple(u32, u32, bool); + let str = "[123,321,true]"; + let expected = TestTuple(123, 321, true); + assert_eq!(expected, from_str(str).unwrap()); + + #[derive(Deserialize, PartialEq, Debug)] + struct Test { + int: u32, + seq: Vec, + tup: (i32, i32, i32), + } + + let str = r#"{"int":1,"seq":["abcd","efgh"],"tup":[1,2,3]}"#; + let expected = Test { + int: 1, + seq: vec![String::from("abcd"), String::from("efgh")], + tup: (1, 2, 3), + }; + assert_eq!(expected, from_str(str).unwrap()); + + // The following is the test for abnormal input of JSON. + let str_abnormal = r#"{"int":1,"seq":["abcd","efgh"],"tup":[1,2,3]"#; + let res = from_str::(str_abnormal); + assert!(res.is_err()); + } + + /// UT test to deserialize enum + /// + /// # Title + /// ut_deserialize_enum + /// + /// # Brief + /// 1.Uses deserializer::from_slice method to deserialize enum. + /// 2.Checks if the test results are correct. + #[test] + fn ut_deserialize_enum() { + #[derive(Deserialize, PartialEq, Debug)] + enum E<'a> { + Unit, + Newtype(u32), + Tuple(u32, u32), + Struct { a: u32 }, + Reference(Cow<'a, str>), + } + + let u = r#""Unit""#.as_bytes(); + let expected = E::Unit; + assert_eq!(expected, from_slice(u).unwrap()); + + let n = r#"{"Newtype":1}"#.as_bytes(); + let expected = E::Newtype(1); + assert_eq!(expected, from_slice(n).unwrap()); + + let t = r#"{"Tuple":[1,2]}"#.as_bytes(); + let expected = E::Tuple(1, 2); + assert_eq!(expected, from_slice(t).unwrap()); + + let s = r#"{"Struct":{"a":1}}"#.as_bytes(); + let expected = E::Struct { a: 1 }; + assert_eq!(expected, from_slice(s).unwrap()); + + let s = r#"{"Reference":"reference"}"#.as_bytes(); + let expected = E::Reference(Cow::from("reference")); + assert_eq!(expected, from_slice(s).unwrap()); + + // The following is the test for abnormal input of JSON. + let slice_abnormal = r#" "#.as_bytes(); + let res = from_slice::(slice_abnormal); + assert!(res.is_err()); + + let slice_abnormal = r#"x"#.as_bytes(); + let res = from_slice::(slice_abnormal); + assert!(res.is_err()); + + let slice_abnormal = r#""Unit"#.as_bytes(); + let res = from_slice::(slice_abnormal); + assert!(res.is_err()); + + let slice_abnormal = r#"{"Newtype" 1"#.as_bytes(); + let res = from_slice::(slice_abnormal); + assert!(res.is_err()); + + let slice_abnormal = r#"{"Newtype":1"#.as_bytes(); + let res = from_slice::(slice_abnormal); + assert!(res.is_err()); + + let slice_abnormal = r#"{"Tuple":[1,2}"#.as_bytes(); + let res = from_slice::(slice_abnormal); + assert!(res.is_err()); + } +} diff --git a/src/encoder.rs b/src/encoder.rs new file mode 100644 index 0000000000000000000000000000000000000000..557b9ec42e38776270fb065a1fa6544769fdc0db --- /dev/null +++ b/src/encoder.rs @@ -0,0 +1,511 @@ +/* + * Copyright (c) 2023 Huawei Device Co., Ltd. + * Licensed 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. + */ + +use crate::{consts::*, Array, Error, JsonValue, Number, Object}; +#[cfg(feature = "c_adapter")] +use std::ffi::CString; +use std::io::Write; + +// todo: Considers extracting Encoder traits. + +/// JSON encoder with additional formats, used to output JsonValue instances in JSON format to the specified location. +/// +/// This encoder will add additional formatting control whitespace characters during encoding. +pub(crate) struct FormattedEncoder<'a, W: Write> { + output: &'a mut W, + /// The current number of nested layers + tab: usize, +} + +impl<'a, W: Write> FormattedEncoder<'a, W> { + /// Creates + pub(crate) fn new(output: &'a mut W) -> Self { + Self { output, tab: 0 } + } + + /// Encodes + pub(crate) fn encode(&mut self, value: &JsonValue) -> Result<(), Error> { + self.encode_value(value)?; + self.output.write_all(LINE_FEED_STR)?; + Ok(()) + } + + /// Encodes JsonValue + fn encode_value(&mut self, value: &JsonValue) -> Result<(), Error> { + match value { + JsonValue::Null => self.encode_null(), + JsonValue::Boolean(boolean) => self.encode_boolean(boolean), + JsonValue::Number(number) => self.encode_number(number), + JsonValue::String(string) => self.encode_string(string), + JsonValue::Array(array) => self.encode_array(array), + JsonValue::Object(object) => self.encode_object(object), + } + } + + /// Add tabs to improve readability. + fn add_tab(&mut self) -> Result<(), Error> { + for _ in 0..self.tab { + self.output.write_all(FOUR_SPACES_STR)?; + } + Ok(()) + } + + /// Encodes Null + fn encode_null(&mut self) -> Result<(), Error> { + encode_null(self.output) + } + + /// Encodes Boolean + fn encode_boolean(&mut self, boolean: &bool) -> Result<(), Error> { + encode_boolean(self.output, *boolean) + } + + /// Encodes Number + fn encode_number(&mut self, number: &Number) -> Result<(), Error> { + encode_number(self.output, number) + } + + /// Encodes Key + fn encode_key(&mut self, key: &str) -> Result<(), Error> { + encode_string(self.output, key) + } + + /// Encodes String + #[cfg(feature = "c_adapter")] + fn encode_string(&mut self, string: &CString) -> Result<(), Error> { + encode_string(self.output, unsafe { + core::str::from_utf8_unchecked(string.as_bytes()) + }) + } + + /// Encodes String + #[cfg(not(feature = "c_adapter"))] + fn encode_string(&mut self, string: &str) -> Result<(), Error> { + encode_string(self.output, string) + } + + /// Encodes Array + fn encode_array(&mut self, array: &Array) -> Result<(), Error> { + // Check whether multiple lines are required. If array or object + // exists in the array value, multiple lines are required. + let mut multiple_line = false; + for v in array.iter() { + if v.is_array() | v.is_object() { + multiple_line = true; + break; + } + } + + self.output.write_all(LEFT_SQUARE_BRACKET_STR)?; + if multiple_line { + self.output.write_all(LINE_FEED_STR)?; + self.tab += 1; + self.add_tab()?; + for (n, v) in array.iter().enumerate() { + if n != 0 { + self.output.write_all(COMMA_STR)?; + self.output.write_all(LINE_FEED_STR)?; + self.add_tab()?; + } + self.encode_value(v)?; + } + self.output.write_all(LINE_FEED_STR)?; + self.tab -= 1; + self.add_tab()?; + } else { + for (n, v) in array.iter().enumerate() { + if n != 0 { + self.output.write_all(COMMA_STR)?; + self.output.write_all(SPACE_STR)?; + } + self.encode_value(v)?; + } + } + self.output.write_all(RIGHT_SQUARE_BRACKET_STR)?; + Ok(()) + } + + /// Encodes Object + fn encode_object(&mut self, object: &Object) -> Result<(), Error> { + self.output.write_all(LEFT_CURLY_BRACKET_STR)?; + self.tab += 1; + for (u, (k, v)) in object.iter().enumerate() { + if u != 0 { + self.output.write_all(COMMA_STR)?; + } + self.output.write_all(LINE_FEED_STR)?; + self.add_tab()?; + self.encode_key(k)?; + self.output.write_all(COLON_STR)?; + self.output.write_all(SPACE_STR)?; + self.encode_value(v)?; + } + self.tab -= 1; + // Non-empty objects require additional newlines and tabs. + if !object.is_empty() { + self.output.write_all(LINE_FEED_STR)?; + self.add_tab()?; + } + self.output.write_all(RIGHT_CURLY_BRACKET_STR)?; + Ok(()) + } +} + +/// JSON encoder that outputs no extra whitespace characters , +/// used to output a JsonValue instance in JSON format to a specified location. +pub(crate) struct CompactEncoder<'a, W: Write> { + output: &'a mut W, +} + +impl<'a, W: Write> CompactEncoder<'a, W> { + /// Creates + pub(crate) fn new(output: &'a mut W) -> Self { + Self { output } + } + + /// Encodes + pub(crate) fn encode(&mut self, value: &JsonValue) -> Result<(), Error> { + self.encode_value(value) + } + + /// Encodes JsonValue + fn encode_value(&mut self, value: &JsonValue) -> Result<(), Error> { + match value { + JsonValue::Null => self.encode_null(), + JsonValue::Boolean(boolean) => self.encode_boolean(boolean), + JsonValue::Number(number) => self.encode_number(number), + JsonValue::String(string) => self.encode_string(string), + JsonValue::Array(array) => self.encode_array(array), + JsonValue::Object(object) => self.encode_object(object), + } + } + + /// Encodes Null + fn encode_null(&mut self) -> Result<(), Error> { + encode_null(self.output) + } + + /// Encodes Boolean + fn encode_boolean(&mut self, boolean: &bool) -> Result<(), Error> { + encode_boolean(self.output, *boolean) + } + + /// Encodes Number + fn encode_number(&mut self, number: &Number) -> Result<(), Error> { + encode_number(self.output, number) + } + + /// Encodes Key + fn encode_key(&mut self, key: &str) -> Result<(), Error> { + encode_string(self.output, key) + } + + /// Encodes String + #[cfg(feature = "c_adapter")] + fn encode_string(&mut self, string: &CString) -> Result<(), Error> { + encode_string(self.output, unsafe { + std::str::from_utf8_unchecked(string.as_bytes()) + }) + } + + /// Encodes String + #[cfg(not(feature = "c_adapter"))] + fn encode_string(&mut self, string: &str) -> Result<(), Error> { + encode_string(self.output, string) + } + + /// Encodes Array + fn encode_array(&mut self, array: &Array) -> Result<(), Error> { + self.output.write_all(LEFT_SQUARE_BRACKET_STR)?; + for (n, v) in array.iter().enumerate() { + if n != 0 { + self.output.write_all(COMMA_STR)?; + } + self.encode_value(v)?; + } + self.output.write_all(RIGHT_SQUARE_BRACKET_STR)?; + Ok(()) + } + + /// Encodes Object + fn encode_object(&mut self, object: &Object) -> Result<(), Error> { + self.output.write_all(LEFT_CURLY_BRACKET_STR)?; + for (u, (k, v)) in object.iter().enumerate() { + if u != 0 { + self.output.write_all(COMMA_STR)?; + } + self.encode_key(k)?; + self.output.write_all(COLON_STR)?; + self.encode_value(v)?; + } + self.output.write_all(RIGHT_CURLY_BRACKET_STR)?; + Ok(()) + } +} + +#[inline] +fn encode_null(writer: &mut dyn Write) -> Result<(), Error> { + writer.write_all(NULL_STR)?; + Ok(()) +} + +#[inline] +fn encode_boolean(writer: &mut dyn Write, boolean: bool) -> Result<(), Error> { + if boolean { + writer.write_all(TRUE_STR)?; + } else { + writer.write_all(FALSE_STR)?; + } + Ok(()) +} + +#[inline] +pub(crate) fn encode_number(writer: &mut dyn Write, number: &Number) -> Result<(), Error> { + write!(writer, "{number}")?; + Ok(()) +} + +#[inline] +fn encode_string(writer: &mut dyn Write, string: &str) -> Result<(), Error> { + writer.write_all(QUOTATION_MARK_STR)?; + encode_string_inner(writer, string)?; + writer.write_all(QUOTATION_MARK_STR)?; + Ok(()) +} + +#[cfg(feature = "ascii_only")] +pub(crate) fn encode_string_inner(writer: &mut dyn Write, string: &str) -> Result<(), Error> { + let bytes = string.as_bytes(); + let len = bytes.len(); + let mut start = 0usize; + + for i in 0..len { + let ch = &bytes[i]; + if ESCAPE[(*ch) as usize] { + writer.write_all(&bytes[start..i])?; + start = i + 1; + + match *ch { + REVERSE_SOLIDUS => writer.write_all(JSON_REVERSE_SOLIDUS)?, + QUOTATION_MARK => writer.write_all(JSON_QUOTATION_MARK)?, + BS_UNICODE_U8 => writer.write_all(JSON_BS)?, + FF_UNICODE_U8 => writer.write_all(JSON_FF)?, + LF_UNICODE_U8 => writer.write_all(JSON_LF)?, + CR_UNICODE_U8 => writer.write_all(JSON_CR)?, + HT_UNICODE_U8 => writer.write_all(JSON_HT)?, + x => write!(writer, "\\u{number:0>width$x}", number = x, width = 4)?, + } + } + } + if start != len { + writer.write_all(&bytes[start..len])?; + } + + Ok(()) +} + +#[cfg(not(feature = "ascii_only"))] +pub(crate) fn encode_string_inner(writer: &mut dyn Write, string: &str) -> Result<(), Error> { + fn split_pattern( + writer: &mut dyn Write, + pattern: &mut &str, + split_pos: &mut usize, + ch: char, + ) -> Result<(), Error> { + let (l, r) = (*pattern).split_at(*split_pos); + writer.write_all(l.as_bytes())?; + *pattern = r; + + let (_, r) = (*pattern).split_at(ch.len_utf8()); + *pattern = r; + *split_pos = 0; + Ok(()) + } + + let mut pattern = string; + let mut split_pos = 0usize; + for ch in string.chars() { + if ch.is_ascii() { + match PRINT_MAP[ch as usize] { + PrintMapItem::Other => { + split_pos += 1; + continue; + } + PrintMapItem::Special(x) => { + split_pattern(writer, &mut pattern, &mut split_pos, ch)?; + writer.write_all(x)?; + } + PrintMapItem::Control => { + split_pattern(writer, &mut pattern, &mut split_pos, ch)?; + let bytes = ch as u32; + write!(writer, "\\u{number:0>width$x}", number = bytes, width = 4)?; + } + } + continue; + } + split_pattern(writer, &mut pattern, &mut split_pos, ch)?; + let bytes = ch as u32; + write!(writer, "\\u{number:0>width$x}", number = bytes, width = 4)?; + } + if split_pos != 0 { + writer.write_all(pattern.as_bytes())?; + } + Ok(()) +} + +#[cfg(test)] +mod ut_encoder { + use crate::{CompactEncoder, FormattedEncoder, JsonValue}; + use std::io::Write; + + struct StringWriter { + string: String, + } + + impl StringWriter { + fn new() -> Self { + Self { + string: String::new(), + } + } + } + + impl Write for StringWriter { + fn write(&mut self, buf: &[u8]) -> std::io::Result { + self.string + .push_str(unsafe { std::str::from_utf8_unchecked(buf) }); + self.flush()?; + Ok(buf.len()) + } + + fn flush(&mut self) -> std::io::Result<()> { + Ok(()) + } + } + + macro_rules! encoder_test_case { + ($encoder: ident, $input: expr, $output: expr $(,)?) => { + let value = JsonValue::from_text($input).unwrap(); + let mut writer = StringWriter::new(); + let mut encoder = $encoder::new(&mut writer); + assert!(encoder.encode(&value).is_ok()); + assert_eq!(writer.string, $output); + }; + } + + /// UT test for `FormattedEncoder`. + /// + /// # Title + /// ut_formatted_encoder + /// + /// # Brief + /// 1. Creates a `JsonValue` called `json_value`. + /// 2. Creates a `FormattedEncoder` called `encoder`. + /// 3. Uses `encoder` to encode `json_value`. + /// 4. Checks if the results are correct. + #[test] + fn ut_formatted_encoder() { + encoder_test_case!( + FormattedEncoder, + "{\"null\":null}", + "{\n \"null\": null\n}\n", + ); + + encoder_test_case!( + FormattedEncoder, + "{\"true\":true}", + "{\n \"true\": true\n}\n", + ); + + encoder_test_case!( + FormattedEncoder, + "{\"false\":false}", + "{\n \"false\": false\n}\n", + ); + + encoder_test_case!( + FormattedEncoder, + "{\"number\":3.14}", + "{\n \"number\": 3.14\n}\n", + ); + + encoder_test_case!( + FormattedEncoder, + "{\"string\":\"HelloWorld\"}", + "{\n \"string\": \"HelloWorld\"\n}\n", + ); + + encoder_test_case!( + FormattedEncoder, + "{\"array\":[1, 2, 3]}", + "{\n \"array\": [1, 2, 3]\n}\n", + ); + + encoder_test_case!( + FormattedEncoder, + "{\"object\":{\"key1\":1}}", + "{\n \"object\": {\n \"key1\": 1\n }\n}\n", + ); + } + + /// UT test for `CompactEncoder`. + /// + /// # Title + /// ut_compact_encoder + /// + /// # Brief + /// 1. Creates a `JsonValue` called `json_value`. + /// 2. Creates a `Compact` called `encoder`. + /// 3. Uses `encoder` to encode `json_value`. + /// 4. Checks if the results are correct. + #[test] + fn ut_compact_encoder() { + encoder_test_case!(CompactEncoder, "{\"null\":null}", "{\"null\":null}",); + + encoder_test_case!(CompactEncoder, "{\"true\":true}", "{\"true\":true}",); + + encoder_test_case!(CompactEncoder, "{\"false\":false}", "{\"false\":false}",); + + encoder_test_case!(CompactEncoder, "{\"number\":3.14}", "{\"number\":3.14}",); + + encoder_test_case!( + CompactEncoder, + "{\"string\":\"HelloWorld\"}", + "{\"string\":\"HelloWorld\"}", + ); + + #[cfg(not(feature = "ascii_only"))] + encoder_test_case!( + CompactEncoder, + "{\"string\":\"\\b\\t\\f\\n\\u0000\\u2764\"}", + "{\"string\":\"\\b\\t\\f\\n\\u0000\\u2764\"}", + ); + + #[cfg(feature = "ascii_only")] + encoder_test_case!( + CompactEncoder, + "{\"string\":\"\\b\\t\\f\\n\\u0000\\u2764\"}", + "{\"string\":\"\\b\\t\\f\\n\\u0000\u{2764}\"}", + ); + + encoder_test_case!(CompactEncoder, "{\"array\":[1,2,3]}", "{\"array\":[1,2,3]}",); + + encoder_test_case!( + CompactEncoder, + "{\"object\":{\"key1\":1,\"key2\":2}}", + "{\"object\":{\"key1\":1,\"key2\":2}}", + ); + } +} diff --git a/src/error.rs b/src/error.rs new file mode 100644 index 0000000000000000000000000000000000000000..ad084fafe71f32a7786e22433c3dcdf5ff9fc7f7 --- /dev/null +++ b/src/error.rs @@ -0,0 +1,416 @@ +/* + * Copyright (c) 2023 Huawei Device Co., Ltd. + * Licensed 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. + */ + +use core::fmt::{Debug, Display, Formatter, Result}; +use std::ffi::IntoStringError; +use std::string::FromUtf8Error; + +/// Errors during parsing. +pub enum Error { + /// Parsing error. + Parsing(ParseError), + + /// Io error. + Io(std::io::Error), + + /// Parse number error. + ParseNumber, + + /// Utf8 transform error. + Utf8Transform, + + /// Type transform error. + TypeTransform, + + /// Reader error. + Reader(Box), + + /// Incorrect serde usage error. + IncorrectSerdeUsage, + + /// Used to convert serde-related errors. + Custom(String), + + /// Exceeds the recursion limit. + ExceedRecursionLimit, +} + +/// The specific location and character of the error during parsing. +pub enum ParseError { + /// Undesired character (line number, character number, current character) + UnexpectedCharacter(usize, usize, char), + + /// Illegal UTF-8 character (line number) + InvalidUtf8Bytes(usize), + + /// Undesired end-of-file character (line number) + UnexpectedEndOfJson(usize), + + /// Expected Eof but not received (line number) + TrailingBytes(usize), + + /// The input sequence has not yet been parsed. + ParsingUnfinished, +} + +impl Error { + pub(crate) fn new_reader>>(e: E) -> Self { + Error::Reader(e.into()) + } +} + +impl Debug for ParseError { + fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result { + match self { + Self::UnexpectedCharacter(line, pos, unexpected) => { + write!( + f, + "[Line]: {line}, [Pos]: {pos}, [Error]: Unexpected character: " + )?; + let mut str = match *unexpected { + '\u{8}' => Some("\'\\b\'"), + '\u{b}' => Some("\'\\v\'"), + '\u{c}' => Some("\'\\f\'"), + _ => None, + }; + if let Some(s) = str.take() { + write!(f, "{s}.") + } else { + write!(f, "{unexpected:?}.") + } + } + Self::InvalidUtf8Bytes(line) => { + write!(f, "[line]: {line}, [Error]: Invalid UTF-8 byte.") + } + Self::UnexpectedEndOfJson(line) => { + write!(f, "[Line]: {line}, [Error]: Unexpected end of json.") + } + Self::TrailingBytes(line) => { + write!(f, "[Line]: {line}, [Error]: Expected end of json but not.") + } + Self::ParsingUnfinished => { + write!(f, "[Error]: Value has not been fully deserialized.") + } + } + } +} + +impl Debug for Error { + fn fmt(&self, f: &mut Formatter<'_>) -> Result { + match self { + Self::Parsing(e) => write!(f, "Parse Error: {e:?}"), + Self::Io(e) => write!(f, "Io Error: {e:?}"), + Self::ParseNumber => write!(f, "Parse Number Error"), + Self::TypeTransform => write!(f, "Type Transform Error"), + Self::Utf8Transform => write!(f, "Utf8 Transform Error"), + Self::IncorrectSerdeUsage => write!(f, "Incorrect Serde Usage Error"), + Self::Custom(s) => write!(f, "{s}"), + Self::Reader(e) => write!(f, "Reader Error:{e:?}"), + Self::ExceedRecursionLimit => write!(f, "Exceed the recursion limit"), + } + } +} + +impl Display for Error { + fn fmt(&self, f: &mut Formatter<'_>) -> Result { + Debug::fmt(self, f) + } +} + +impl From for Error { + fn from(e: ParseError) -> Self { + Error::Parsing(e) + } +} + +impl From for Error { + fn from(_: core::str::Utf8Error) -> Self { + Error::Utf8Transform + } +} + +impl From for Error { + fn from(e: std::io::Error) -> Self { + Error::Io(e) + } +} + +impl std::error::Error for Error {} + +impl serde::ser::Error for Error { + fn custom(msg: T) -> Self + where + T: Display, + { + Error::Custom(msg.to_string()) + } +} + +impl serde::de::Error for Error { + fn custom(msg: T) -> Self + where + T: Display, + { + Error::Custom(msg.to_string()) + } +} + +impl From for Error { + fn from(_: FromUtf8Error) -> Self { + Error::Utf8Transform + } +} + +impl From for Error { + fn from(_: IntoStringError) -> Self { + Error::TypeTransform + } +} + +#[cfg(test)] +mod ut_error { + use crate::{Error, ParseError}; + use std::ffi::CString; + use std::io::ErrorKind; + + /// UT test for `Error::fmt`. + /// + /// # Title + /// ut_error_fmt + /// + /// # Brief + /// 1. Creates a `Error`. + /// 2. Calls `Error::fmt` on this error. + /// 3. Checks if the results are correct. + #[test] + fn ut_error_fmt() { + assert_eq!( + format!( + "{:?}", + Error::Parsing(ParseError::UnexpectedCharacter(1, 1, 'a')) + ), + "Parse Error: [Line]: 1, [Pos]: 1, [Error]: Unexpected character: 'a'.", + ); + + assert_eq!( + format!( + "{:?}", + Error::Parsing(ParseError::UnexpectedCharacter(1, 1, '\u{8}')) + ), + "Parse Error: [Line]: 1, [Pos]: 1, [Error]: Unexpected character: '\\b'.", + ); + + assert_eq!( + format!( + "{:?}", + Error::Parsing(ParseError::UnexpectedCharacter(1, 1, '\u{b}')) + ), + "Parse Error: [Line]: 1, [Pos]: 1, [Error]: Unexpected character: '\\v'.", + ); + + assert_eq!( + format!( + "{:?}", + Error::Parsing(ParseError::UnexpectedCharacter(1, 1, '\u{c}')) + ), + "Parse Error: [Line]: 1, [Pos]: 1, [Error]: Unexpected character: '\\f'.", + ); + + assert_eq!( + format!("{:?}", Error::Parsing(ParseError::InvalidUtf8Bytes(1))), + "Parse Error: [line]: 1, [Error]: Invalid UTF-8 byte.", + ); + + assert_eq!( + format!("{:?}", Error::Parsing(ParseError::UnexpectedEndOfJson(1))), + "Parse Error: [Line]: 1, [Error]: Unexpected end of json.", + ); + + assert_eq!( + format!("{:?}", Error::Parsing(ParseError::TrailingBytes(1))), + "Parse Error: [Line]: 1, [Error]: Expected end of json but not.", + ); + + assert_eq!( + format!("{:?}", Error::Parsing(ParseError::ParsingUnfinished)), + "Parse Error: [Error]: Value has not been fully deserialized.", + ); + + assert_eq!( + format!( + "{:?}", + Error::Io(std::io::Error::from(ErrorKind::AddrInUse)) + ), + "Io Error: Kind(AddrInUse)", + ); + + assert_eq!(format!("{:?}", Error::ParseNumber), "Parse Number Error",); + + assert_eq!( + format!("{:?}", Error::Utf8Transform), + "Utf8 Transform Error", + ); + + assert_eq!( + format!("{:?}", Error::TypeTransform), + "Type Transform Error", + ); + + assert_eq!( + format!("{:?}", Error::IncorrectSerdeUsage), + "Incorrect Serde Usage Error", + ); + + assert_eq!( + format!("{:?}", Error::Custom(String::from("Custom Error"))), + "Custom Error", + ); + + assert_eq!( + format!("{:?}", Error::ExceedRecursionLimit), + "Exceed the recursion limit", + ); + + assert_eq!( + format!( + "{:?}", + Error::Reader(std::io::Error::from(ErrorKind::AddrInUse).into()) + ), + "Reader Error:Kind(AddrInUse)", + ); + + assert_eq!( + format!( + "{}", + Error::Parsing(ParseError::UnexpectedCharacter(1, 1, 'a')) + ), + "Parse Error: [Line]: 1, [Pos]: 1, [Error]: Unexpected character: 'a'.", + ); + + assert_eq!( + format!( + "{}", + Error::Parsing(ParseError::UnexpectedCharacter(1, 1, '\u{8}')) + ), + "Parse Error: [Line]: 1, [Pos]: 1, [Error]: Unexpected character: '\\b'.", + ); + + assert_eq!( + format!( + "{}", + Error::Parsing(ParseError::UnexpectedCharacter(1, 1, '\u{b}')) + ), + "Parse Error: [Line]: 1, [Pos]: 1, [Error]: Unexpected character: '\\v'.", + ); + + assert_eq!( + format!( + "{}", + Error::Parsing(ParseError::UnexpectedCharacter(1, 1, '\u{c}')) + ), + "Parse Error: [Line]: 1, [Pos]: 1, [Error]: Unexpected character: '\\f'.", + ); + + assert_eq!( + format!("{}", Error::Parsing(ParseError::InvalidUtf8Bytes(1))), + "Parse Error: [line]: 1, [Error]: Invalid UTF-8 byte.", + ); + + assert_eq!( + format!("{}", Error::Parsing(ParseError::UnexpectedEndOfJson(1))), + "Parse Error: [Line]: 1, [Error]: Unexpected end of json.", + ); + + assert_eq!( + format!("{}", Error::Parsing(ParseError::TrailingBytes(1))), + "Parse Error: [Line]: 1, [Error]: Expected end of json but not.", + ); + + assert_eq!( + format!("{}", Error::Io(std::io::Error::from(ErrorKind::AddrInUse))), + "Io Error: Kind(AddrInUse)", + ); + + assert_eq!(format!("{}", Error::ParseNumber), "Parse Number Error",); + + assert_eq!(format!("{}", Error::Utf8Transform), "Utf8 Transform Error",); + + assert_eq!(format!("{}", Error::TypeTransform), "Type Transform Error",); + + assert_eq!( + format!( + "{}", + Error::Reader(std::io::Error::from(ErrorKind::AddrInUse).into()) + ), + "Reader Error:Kind(AddrInUse)", + ); + } + + /// UT test for `Error::from`. + /// + /// # Title + /// ut_error_from + /// + /// # Brief + /// 1. Creates some other errors. + /// 2. Calls `Error::from` on those error. + /// 3. Checks if the results are correct. + #[test] + fn ut_error_from() { + assert_eq!( + format!("{}", Error::from(ParseError::TrailingBytes(1))), + "Parse Error: [Line]: 1, [Error]: Expected end of json but not.", + ); + + assert_eq!( + format!( + "{}", + Error::from(std::io::Error::from(ErrorKind::AddrInUse)) + ), + "Io Error: Kind(AddrInUse)", + ); + + let str_vec = [0b10000000u8; 1]; + assert_eq!( + format!( + "{}", + Error::from(std::str::from_utf8(&str_vec).err().unwrap()) + ), + "Utf8 Transform Error", + ); + + assert_eq!( + format!( + "{}", + Error::from(String::from_utf8(vec![129, 129, 129]).err().unwrap()) + ), + "Utf8 Transform Error", + ); + + assert_eq!( + format!( + "{}", + Error::from( + CString::new(vec![129, 129, 129]) + .expect("CString::new failed") + .into_string() + .err() + .unwrap() + ) + ), + "Type Transform Error", + ); + } +} diff --git a/src/lib.rs b/src/lib.rs new file mode 100644 index 0000000000000000000000000000000000000000..5d9ec751fe222883b09de8e9b4c811a87a08662c --- /dev/null +++ b/src/lib.rs @@ -0,0 +1,86 @@ +/* + * Copyright (c) 2023 Huawei Device Co., Ltd. + * Licensed 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. + */ + +//! ylong_json is a library for parsing and serializing JSON in Rust. +//! This library provides a convenient macro-based API for creating +//! JSON values, and utilities for reading and writing JSON data +//! from various sources. + +// TODO: 1) Isolates ylong_json no_std. +// TODO: 2) Handles illegal Utf-8 bytes. +// TODO: 3) Refactors ylong_json. +// TODO: 4) JsonValue provides 'Contains' methods。 + +/// Creates an array with at least one but any number of elements. +#[macro_export] +macro_rules! array { + () => ({ + Array::new() + }); + ($($x:expr),+ $(,)?) => ({ + let mut array = Array::new(); + $( + array.push($x.into()); + )* + array + }); +} + +/// Creates an object with at least one but any number of key-value pairs. +#[macro_export] +macro_rules! object { + () => ({ + Object::new() + }); + ($($k: expr => $v: expr);+ $(;)?) => ({ + let mut object = Object::new(); + $( + object.insert(String::from($k), $v.into()); + )* + object + }); +} + +mod consts; +mod encoder; +mod error; +mod reader; +#[macro_use] +mod states; +mod value; + +pub use error::{Error, ParseError}; +pub use serializer_compact::to_string; +pub use value::{Array, Index, JsonValue, Number, Object}; + +pub(crate) use encoder::{CompactEncoder, FormattedEncoder}; +pub(crate) use states::start_parsing; + +#[cfg(feature = "c_adapter")] +mod adapter; +#[cfg(feature = "c_adapter")] +pub use adapter::*; + +mod deserializer; +#[cfg(any(feature = "list_array", feature = "list_object"))] +mod linked_list; +mod serializer_compact; + +#[cfg(any(feature = "list_array", feature = "list_object"))] +pub(crate) use linked_list::{Cursor, CursorMut, LinkedList}; +#[cfg(any(feature = "list_array", feature = "list_object"))] +pub use linked_list::{Iter, IterMut, Node}; + +pub use deserializer::{from_reader, from_slice, from_str}; diff --git a/src/linked_list.rs b/src/linked_list.rs new file mode 100644 index 0000000000000000000000000000000000000000..bea80d4492784efb6ee5dcb22fd1c42a13893f20 --- /dev/null +++ b/src/linked_list.rs @@ -0,0 +1,979 @@ +/* + * Copyright (c) 2023 Huawei Device Co., Ltd. + * Licensed 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. + */ + +use core::fmt::{Debug, Formatter}; +use core::marker::PhantomData; +use core::ptr::null; + +// todo: Considers deleting PhantomData. + +/// Linked list implementation, provides two sets of methods for getting nodes and members. +/// Only tail insertion, reading, and eject are supported. +pub(crate) struct LinkedList { + head: *const Node, + tail: *const Node, + len: usize, + marker: PhantomData>>, +} + +impl LinkedList { + /// Creates LinkedList. + pub(crate) const fn new() -> Self { + LinkedList { + head: null(), + tail: null(), + len: 0, + marker: PhantomData, + } + } + + /// Gets length of the list. + #[inline] + pub(crate) fn len(&self) -> usize { + self.len + } + + /// Determines whether the linked list is empty. + #[inline] + pub(crate) fn is_empty(&self) -> bool { + self.len == 0 + } + + /// Inserts an element at the end of the list + pub(crate) fn push_back(&mut self, value: T) { + let mut node = Box::new(Node::new(value)); + unsafe { + // Sets prev to LinkedList.tail + node.prev = self.tail; + // Gets an internal element pointer. + let node = Box::leak(node) as *const Node; + + if self.tail.is_null() { + self.head = node; + } else { + (*(self.tail as *mut Node)).next = node; + } + + self.tail = node; + self.len += 1; + } + } + + /// Pops an element from the end of the list. + pub(crate) fn pop_back(&mut self) -> Option { + if self.tail.is_null() { + None + } else { + unsafe { + let node = Box::from_raw(self.tail as *mut Node); + self.tail = node.prev; + + if self.tail.is_null() { + self.head = null(); + } else { + (*(self.tail as *mut Node)).next = null(); + } + + self.len -= 1; + Some(node.into_element()) + } + } + } + + /// Gets an ordinary iterator for a linked list. + #[inline] + pub(crate) fn iter(&self) -> Iter<'_, T> { + Iter { + head: self.head, + tail: self.tail, + len: self.len, + marker: PhantomData, + } + } + + /// Gets a mutable iterator for the linked list. + #[inline] + pub(crate) fn iter_mut(&mut self) -> IterMut<'_, T> { + IterMut { + head: self.head, + tail: self.tail, + len: self.len, + marker: PhantomData, + } + } + + /// Gets the normal cursor of the list and sets the starting point to the list header. + #[inline] + pub(crate) fn cursor_front(&self) -> Cursor<'_, T> { + Cursor { + index: 0, + current: self.head, + list: self, + } + } + + /// Gets the variable cursor of the list and sets the starting point to the list header. + #[inline] + pub(crate) fn cursor_front_mut(&mut self) -> CursorMut<'_, T> { + CursorMut { + index: 0, + current: self.head, + list: self, + } + } + + /// Gets the normal cursor of the list and sets the starting point to the end of the list. + #[cfg(feature = "list_array")] + #[inline] + pub(crate) fn cursor_back(&self) -> Cursor<'_, T> { + Cursor { + index: self.len.saturating_sub(1), + current: self.tail, + list: self, + } + } + + /// Gets the variable cursor of the list and sets the start to the end of the list. + #[cfg(feature = "list_array")] + #[inline] + pub(crate) fn cursor_back_mut(&mut self) -> CursorMut<'_, T> { + CursorMut { + index: self.len.saturating_sub(1), + current: self.tail, + list: self, + } + } + + /// Gets a mutable reference to the tail element of the list. + #[cfg(feature = "list_array")] + #[inline] + pub(crate) fn back(&self) -> Option<&T> { + if self.tail.is_null() { + None + } else { + unsafe { Some(&(*self.tail).element) } + } + } + + /// Gets a mutable reference to the tail element of the list. + #[inline] + pub(crate) fn back_mut(&mut self) -> Option<&mut T> { + if self.tail.is_null() { + None + } else { + unsafe { Some(&mut (*(self.tail as *mut Node)).element) } + } + } + + /// Gets a common reference to the node at the end of the linked list. + #[cfg(feature = "list_array")] + #[inline] + pub(crate) fn back_node(&self) -> Option<&Node> { + if self.tail.is_null() { + None + } else { + unsafe { Some(&(*self.tail)) } + } + } + + /// Gets a mutable reference to the node at the end of the linked list. + #[cfg(feature = "list_array")] + #[inline] + pub(crate) fn back_node_mut(&mut self) -> Option<&mut Node> { + if self.tail.is_null() { + None + } else { + unsafe { + // Sets node.parent to the current linked_list in order to delete node. + let node = &mut *(self.tail as *mut Node); + node.parent = self as *const LinkedList; + Some(node) + } + } + } + + /// Removes a node from the linked list. + pub(crate) unsafe fn unlink_node(&mut self, node: *const Node) { + let node = &mut (*(node as *mut Node)); + + if node.prev.is_null() { + self.head = node.next; + } else { + (*(node.prev as *mut Node)).next = node.next; + } + + if node.next.is_null() { + self.tail = node.prev; + } else { + (*(node.next as *mut Node)).prev = node.prev; + } + + self.len -= 1; + } +} + +impl Default for LinkedList { + fn default() -> Self { + Self::new() + } +} + +impl Debug for LinkedList { + fn fmt(&self, f: &mut Formatter<'_>) -> core::fmt::Result { + for (n, item) in self.iter().enumerate() { + if n != 0 { + write!(f, ",")?; + } + write!(f, "{item:?}")?; + } + Ok(()) + } +} + +impl PartialEq for LinkedList { + fn eq(&self, other: &Self) -> bool { + if self.len != other.len { + return false; + } + for (a, b) in self.iter().zip(other.iter()) { + if a.ne(b) { + return false; + } + } + true + } +} + +impl Clone for LinkedList { + fn clone(&self) -> Self { + let mut new_list = LinkedList::new(); + for item in self.iter() { + new_list.push_back(item.clone()); + } + new_list + } +} + +impl Drop for LinkedList { + fn drop(&mut self) { + while self.len != 0 { + let _ = self.pop_back(); + } + } +} + +// We need to use static to store the JsonValue, so we need to make the LinkedList implement Send and Sync. +// However, when using this list, locking is still required under concurrent conditions. +unsafe impl Send for LinkedList {} +unsafe impl Sync for LinkedList {} + +/// Linked list node, only through a linked list cursor to get the node. +pub struct Node { + next: *const Node, + prev: *const Node, + parent: *const LinkedList, + element: T, +} + +impl Node { + /// Creates a linked list node. + pub(crate) fn new(element: T) -> Self { + Node { + next: null(), + prev: null(), + parent: null(), + element, + } + } + + /// Retrieves the member inside the list node. + pub(crate) fn into_element(self) -> T { + self.element + } + + /// Gets a common reference to an internal member of a linked list node. + pub(crate) fn get_element_mut(&mut self) -> &mut T { + &mut self.element + } + + /// Removes the node itself from the linked list and returns the member below. + #[cfg(feature = "c_adapter")] + pub(crate) fn remove_self(&mut self) -> Option { + let list = unsafe { &mut *(self.parent as *mut LinkedList) }; + let mut cursor = CursorMut { + index: 0, + current: self as *const Node, + list, + }; + cursor.remove_current() + } +} + +/// A common iterator of a linked list. +pub struct Iter<'a, T: 'a> { + head: *const Node, + tail: *const Node, + len: usize, + marker: PhantomData<&'a Node>, +} + +impl<'a, T> Iterator for Iter<'a, T> { + type Item = &'a T; + + #[inline] + fn next(&mut self) -> Option<&'a T> { + if self.len == 0 || self.head.is_null() { + None + } else { + let node = unsafe { &*(self.head as *mut Node) }; + self.len -= 1; + self.head = node.next; + Some(&node.element) + } + } + + #[inline] + fn size_hint(&self) -> (usize, Option) { + // Returns a tuple representing the remaining range of iterators. + (self.len, Some(self.len)) + } + + #[inline] + fn last(mut self) -> Option<&'a T> { + // Uses the iterator traversal and returns the last element. + self.next_back() + } +} + +impl<'a, T> DoubleEndedIterator for Iter<'a, T> { + fn next_back(&mut self) -> Option<&'a T> { + if self.len == 0 || self.tail.is_null() { + None + } else { + let node = unsafe { &*(self.tail as *mut Node) }; + self.len -= 1; + self.tail = node.prev; + Some(&node.element) + } + } +} + +/// A variable iterator of a linked list. +pub struct IterMut<'a, T: 'a> { + head: *const Node, + tail: *const Node, + len: usize, + marker: PhantomData<&'a mut Node>, +} + +impl<'a, T> Iterator for IterMut<'a, T> { + type Item = &'a mut T; + + #[inline] + fn next(&mut self) -> Option<&'a mut T> { + if self.len == 0 || self.head.is_null() { + None + } else { + let node = unsafe { &mut *(self.head as *mut Node) }; + self.len -= 1; + self.head = node.next; + Some(&mut node.element) + } + } + + #[inline] + fn size_hint(&self) -> (usize, Option) { + // Returns a tuple representing the remaining range of iterators. + (self.len, Some(self.len)) + } + + #[inline] + fn last(mut self) -> Option<&'a mut T> { + // Uses the iterator traversal and returns the last element. + self.next_back() + } +} + +impl<'a, T> DoubleEndedIterator for IterMut<'a, T> { + #[inline] + fn next_back(&mut self) -> Option<&'a mut T> { + if self.len == 0 || self.tail.is_null() { + None + } else { + let node = unsafe { &mut *(self.tail as *mut Node) }; + self.len -= 1; + self.tail = node.prev; + Some(&mut node.element) + } + } +} + +/// A common cursor for a linked list. When the list is empty, +/// it points to a virtual location (pointing to a node that does not actually exist). +pub(crate) struct Cursor<'a, T: 'a> { + index: usize, + current: *const Node, + list: &'a LinkedList, +} + +impl<'a, T> Cursor<'a, T> { + /// Gets the position the cursor is pointing to. + /// If the cursor points to a virtual position, return None. + #[inline] + pub(crate) fn index(&self) -> Option { + if self.current.is_null() { + return None; + } + Some(self.index) + } + + /// The cursor moves back. + #[inline] + pub(crate) fn move_next(&mut self) { + if self.current.is_null() { + self.current = self.list.head; + self.index = 0; + } else { + self.current = unsafe { (*self.current).next }; + self.index += 1; + } + } + + /// The cursor moves forward. + #[cfg(feature = "list_array")] + #[inline] + pub(crate) fn move_prev(&mut self) { + if self.current.is_null() { + self.current = self.list.tail; + self.index = self.list.len().saturating_sub(1); + } else { + self.current = unsafe { (*self.current).prev }; + self.index = self.index.checked_sub(1).unwrap_or_else(|| self.list.len()); + } + } + + /// Gets the cursor. + #[inline] + pub(crate) fn current(&self) -> Option<&'a T> { + if self.current.is_null() { + None + } else { + unsafe { Some(&(*self.current).element) } + } + } + + /// Gets a reference to the current node. + #[inline] + pub(crate) fn current_node(&self) -> Option<&'a Node> { + if self.current.is_null() { + None + } else { + unsafe { Some(&*(self.current)) } + } + } + + #[cfg(feature = "list_object")] + #[inline] + pub(crate) fn current_node_ptr(&self) -> *const Node { + self.current + } +} + +pub(crate) struct CursorMut<'a, T: 'a> { + index: usize, + current: *const Node, + list: &'a mut LinkedList, +} + +impl<'a, T> CursorMut<'a, T> { + /// Gets the index. + #[inline] + pub(crate) fn index(&self) -> Option { + if self.current.is_null() { + return None; + } + Some(self.index) + } + + /// The cursor moves beck. + #[inline] + pub(crate) fn move_next(&mut self) { + if self.current.is_null() { + self.current = self.list.head; + self.index = 0; + } else { + self.current = unsafe { (*self.current).next }; + self.index += 1; + } + } + + /// The cursor moves forward. + #[cfg(feature = "list_array")] + #[inline] + pub(crate) fn move_prev(&mut self) { + if self.current.is_null() { + self.current = self.list.tail; + self.index = self.list.len().saturating_sub(1); + } else { + self.current = unsafe { (*self.current).prev }; + self.index = self.index.checked_sub(1).unwrap_or_else(|| self.list.len()); + } + } + + /// Gets a mutable reference to the current element. + #[cfg(feature = "list_object")] + #[inline] + pub(crate) fn current(&mut self) -> Option<&mut T> { + if self.current.is_null() { + None + } else { + unsafe { Some(&mut (*(self.current as *mut Node)).element) } + } + } + + /// Gets a mutable reference to the current node. + #[inline] + pub(crate) fn current_node(&mut self) -> Option<&'a mut Node> { + if self.current.is_null() { + None + } else { + unsafe { + let node = &mut *(self.current as *mut Node); + node.parent = self.list as *mut LinkedList; + Some(node) + } + } + } + + /// Deletes the node to which the cursor is pointing. + #[inline] + pub(crate) fn remove_current(&mut self) -> Option { + if self.current.is_null() { + return None; + } + + let unlinked_node = self.current; + unsafe { + self.current = (*unlinked_node).next; + self.list.unlink_node(unlinked_node); + let unlinked_node = Box::from_raw(unlinked_node as *mut Node); + Some(unlinked_node.element) + } + } +} + +#[cfg(test)] +mod ut_linked_list { + use crate::LinkedList; + + /// UT test for `LinkedList::pop_back`. + /// + /// # Title + /// ut_linked_list_pop_back + /// + /// # Brief + /// 1. Creates a `LinkedList`. + /// 2. Calls `LinkedList::pop_back` on it. + /// 3. Checks if the test results are correct. + #[test] + fn ut_linked_list_pop_back() { + let mut list = LinkedList::new(); + assert_eq!(list.pop_back(), None); + + list.push_back(1i32); + assert_eq!(list.pop_back(), Some(1)); + } + + /// UT test for `LinkedList::iter_mut`. + /// + /// # Title + /// ut_linked_list_iter_mut + /// + /// # Brief + /// 1. Creates a `LinkedList`. + /// 2. Calls `LinkedList::iter_mut` on it. + /// 3. Checks if the test results are correct. + #[test] + fn ut_linked_list_iter_mut() { + let mut list = LinkedList::new(); + list.push_back(1i32); + list.push_back(2i32); + + let mut iter = list.iter_mut(); + assert_eq!(iter.next(), Some(&mut 1)); + assert_eq!(iter.next(), Some(&mut 2)); + assert_eq!(iter.next(), None); + } + + /// UT test for `LinkedList::back`. + /// + /// # Title + /// ut_linked_list_back + /// + /// # Brief + /// 1. Creates a `LinkedList`. + /// 2. Calls `LinkedList::back` on it. + /// 3. Checks if the test results are correct. + #[cfg(feature = "list_array")] + #[test] + fn ut_linked_list_back() { + let mut list = LinkedList::new(); + assert_eq!(list.back(), None); + + list.push_back(1i32); + assert_eq!(list.back(), Some(&1)); + } + + /// UT test for `LinkedList::back_mut`. + /// + /// # Title + /// ut_linked_list_back_mut + /// + /// # Brief + /// 1. Creates a `LinkedList`. + /// 2. Calls `LinkedList::back_mut` on it. + /// 3. Checks if the test results are correct. + #[test] + fn ut_linked_list_back_mut() { + let mut list = LinkedList::new(); + assert_eq!(list.back_mut(), None); + + list.push_back(1i32); + assert_eq!(list.back_mut(), Some(&mut 1)); + } + + /// UT test for `LinkedList::back_node`. + /// + /// # Title + /// ut_linked_list_back_node + /// + /// # Brief + /// 1. Creates a `LinkedList`. + /// 2. Calls `LinkedList::back_node` on it. + /// 3. Checks if the test results are correct. + #[cfg(feature = "list_array")] + #[test] + fn ut_linked_list_back_node() { + let mut list = LinkedList::new(); + assert!(list.back_node().is_none()); + + list.push_back(1i32); + assert!(list.back_node().is_some()); + } + + /// UT test for `LinkedList::back_node_mut`. + /// + /// # Title + /// ut_linked_list_back_node_mut + /// + /// # Brief + /// 1. Creates a `LinkedList`. + /// 2. Calls `LinkedList::back_node_mut` on it. + /// 3. Checks if the test results are correct. + #[cfg(feature = "list_array")] + #[test] + fn ut_linked_list_back_node_mut() { + let mut list = LinkedList::new(); + assert!(list.back_node_mut().is_none()); + + list.push_back(1i32); + assert!(list.back_node_mut().is_some()); + } + + /// UT test for `LinkedList::default`. + /// + /// # Title + /// ut_linked_list_default + /// + /// # Brief + /// 1. Calls `LinkedList::default` to create a `LinkedList`. + /// 2. Checks if the test results are correct. + #[test] + fn ut_linked_list_default() { + assert_eq!(LinkedList::::default(), LinkedList::::new()); + } + + /// UT test for `LinkedList::eq`. + /// + /// # Title + /// ut_linked_list_eq + /// + /// # Brief + /// 1. Creates some `LinkedList`s. + /// 2. Calls `LinkedList::eq` on them. + /// 3. Checks if the test results are correct. + #[test] + fn ut_linked_list_eq() { + let mut list1 = LinkedList::new(); + list1.push_back(1i32); + + let mut list2 = LinkedList::new(); + list2.push_back(1i32); + list2.push_back(2i32); + + let mut list3 = LinkedList::new(); + list3.push_back(1i32); + list3.push_back(3i32); + + assert_eq!(list1, list1); + assert_ne!(list1, list2); + assert_ne!(list2, list3); + } + + /// UT test for `LinkedList::clone`. + /// + /// # Title + /// ut_linked_list_clone + /// + /// # Brief + /// 1. Creates a `LinkedList`. + /// 2. Calls `LinkedList::clone` to create a copy of it. + /// 3. Checks if the test results are correct. + #[test] + fn ut_linked_list_clone() { + let mut list1 = LinkedList::new(); + list1.push_back(1i32); + + let list2 = list1.clone(); + assert_eq!(list1, list2); + } + + /// UT test for `LinkedList::fmt`. + /// + /// # Title + /// ut_linked_list_fmt + /// + /// # Brief + /// 1. Creates a `LinkedList`. + /// 2. Calls `LinkedList::fmt` on it. + /// 3. Checks if the test results are correct. + #[test] + fn ut_linked_list_fmt() { + let mut list = LinkedList::new(); + list.push_back(1i32); + list.push_back(2i32); + assert_eq!(format!("{list:?}"), "1,2"); + } + + /// UT test for `Cursor::index`. + /// + /// # Title + /// ut_cursor_index + /// + /// # Brief + /// 1. Creates a `LinkedList` and a `Cursor`. + /// 2. Calls `Cursor::index`. + /// 3. Checks if the test results are correct. + #[test] + fn ut_cursor_index() { + let mut list = LinkedList::new(); + list.push_back(1i32); + + let mut cursor = list.cursor_front(); + assert_eq!(cursor.index(), Some(0)); + + cursor.move_next(); + assert_eq!(cursor.index(), None); + } + + /// UT test for `Cursor::move_next`. + /// + /// # Title + /// ut_cursor_move_next + /// + /// # Brief + /// 1. Creates a `LinkedList` and a `Cursor`. + /// 2. Calls `Cursor::move_next`. + /// 3. Checks if the test results are correct. + #[test] + fn ut_cursor_move_next() { + let mut list = LinkedList::new(); + list.push_back(1i32); + list.push_back(2i32); + + let mut cursor = list.cursor_front(); + assert_eq!(cursor.current(), Some(&1)); + + cursor.move_next(); + assert_eq!(cursor.current(), Some(&2)); + + cursor.move_next(); + assert_eq!(cursor.current(), None); + + cursor.move_next(); + assert_eq!(cursor.current(), Some(&1)); + } + + /// UT test for `Cursor::move_prev`. + /// + /// # Title + /// ut_cursor_move_prev + /// + /// # Brief + /// 1. Creates a `LinkedList` and a `Cursor`. + /// 2. Calls `Cursor::move_prev`. + /// 3. Checks if the test results are correct. + #[cfg(feature = "list_array")] + #[test] + fn ut_cursor_move_prev() { + let mut list = LinkedList::new(); + list.push_back(1i32); + list.push_back(2i32); + + let mut cursor = list.cursor_front(); + assert_eq!(cursor.current(), Some(&1)); + + cursor.move_prev(); + assert_eq!(cursor.current(), None); + + cursor.move_prev(); + assert_eq!(cursor.current(), Some(&2)); + + cursor.move_prev(); + assert_eq!(cursor.current(), Some(&1)); + } + + /// UT test for `Cursor::current_node`. + /// + /// # Title + /// ut_cursor_current_node + /// + /// # Brief + /// 1. Creates a `LinkedList` and a `Cursor`. + /// 2. Calls `Cursor::current_node`. + /// 3. Checks if the test results are correct. + #[test] + fn ut_cursor_current_node() { + let mut list = LinkedList::new(); + list.push_back(1i32); + + let mut cursor = list.cursor_front(); + assert!(cursor.current_node().is_some()); + + cursor.move_next(); + assert!(cursor.current_node().is_none()); + } + + /// UT test for `CursorMut::index`. + /// + /// # Title + /// ut_cursor_mut_index + /// + /// # Brief + /// 1. Creates a `LinkedList` and a `CursorMut`. + /// 2. Calls `CursorMut::index`. + /// 3. Checks if the test results are correct. + #[test] + fn ut_cursor_mut_index() { + let mut list = LinkedList::new(); + list.push_back(1i32); + + let mut cursor = list.cursor_front_mut(); + assert_eq!(cursor.index(), Some(0)); + + cursor.move_next(); + assert_eq!(cursor.index(), None); + } + + /// UT test for `CursorMut::move_next`. + /// + /// # Title + /// ut_cursor_mut_move_next + /// + /// # Brief + /// 1. Creates a `LinkedList` and a `CursorMut`. + /// 2. Calls `CursorMut::move_next`. + /// 3. Checks if the test results are correct. + #[test] + fn ut_cursor_mut_move_next() { + let mut list = LinkedList::new(); + list.push_back(1i32); + + let mut cursor = list.cursor_front_mut(); + assert!(cursor.current_node().is_some()); + + cursor.move_next(); + assert!(cursor.current_node().is_none()); + + cursor.move_next(); + assert!(cursor.current_node().is_some()); + } + + /// UT test for `CursorMut::move_prev`. + /// + /// # Title + /// ut_cursor_mut_move_prev + /// + /// # Brief + /// 1. Creates a `LinkedList` and a `CursorMut`. + /// 2. Calls `CursorMut::move_prev`. + /// 3. Checks if the test results are correct. + #[cfg(feature = "list_array")] + #[test] + fn ut_cursor_mut_move_prev() { + let mut list = LinkedList::new(); + list.push_back(1i32); + + let mut cursor = list.cursor_front_mut(); + assert!(cursor.current_node().is_some()); + + cursor.move_prev(); + assert!(cursor.current_node().is_none()); + + cursor.move_prev(); + assert!(cursor.current_node().is_some()); + } + + /// UT test for `CursorMut::current`. + /// + /// # Title + /// ut_cursor_mut_current + /// + /// # Brief + /// 1. Creates a `LinkedList` and a `CursorMut`. + /// 2. Calls `CursorMut::current`. + /// 3. Checks if the test results are correct. + #[cfg(feature = "list_object")] + #[test] + fn ut_cursor_mut_current() { + let mut list = LinkedList::new(); + list.push_back(1i32); + + let mut cursor = list.cursor_front_mut(); + assert_eq!(cursor.current(), Some(&mut 1)); + + cursor.move_next(); + assert_eq!(cursor.current(), None); + } + + /// UT test for `CursorMut::current`. + /// + /// # Title + /// ut_cursor_mut_current + /// + /// # Brief + /// 1. Creates a `LinkedList` and a `CursorMut`. + /// 2. Calls `CursorMut::current`. + /// 3. Checks if the test results are correct. + #[test] + fn ut_cursor_mut_remove_current() { + let mut list = LinkedList::new(); + list.push_back(1i32); + + let mut cursor = list.cursor_front_mut(); + assert_eq!(cursor.remove_current(), Some(1)); + assert_eq!(cursor.remove_current(), None); + } +} diff --git a/src/reader/io_reader.rs b/src/reader/io_reader.rs new file mode 100644 index 0000000000000000000000000000000000000000..0566b78a272f1927646709b5526ede11ca6d8558 --- /dev/null +++ b/src/reader/io_reader.rs @@ -0,0 +1,608 @@ +/* + * Copyright (c) 2023 Huawei Device Co., Ltd. + * Licensed 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. + */ + +use super::{BytesReader, Cacheable, Position}; +use std::io::{Error, ErrorKind, Read, Result}; + +/// Reader for reading I\O. This reader implements `BytesReader` trait and +/// `Cacheable` trait. +/// +/// # Examples +/// ```not run +/// use std::fs::File; +/// use ylong_bytes_reader::{IoReader, BytesReader}; +/// +/// let file = File::open("./test.txt").unwrap(); +/// let mut io_reader = IoReader::new(file); +/// let char = io_reader.next(); +/// let char = io_reader.peek(); +/// ``` +pub(crate) struct IoReader { + io: R, + buf: Vec, // Buffer for storing read bytes. + cur: usize, // The position of the cursor in the current buf. + idx: usize, // A counter of all bytes that have been read. + pos: Position, + cache: Option, +} + +// A simple cache implementation for `IoReader`. +struct Cache { + cache: Vec, + pre: usize, // Last cached location. +} + +impl Cache { + /// Create a new `Cache`. + fn new() -> Self { + Self { + cache: Vec::new(), + pre: 0, + } + } +} + +impl IoReader { + /// Create a new `IoReader` from the given I\O. + pub(crate) fn new(io: R) -> Self { + Self { + io, + buf: Vec::with_capacity(1024), // Default size is 1024. + cur: 0, + idx: 0, + pos: Position::new(1, 1), + cache: None, + } + } + + // Try to read some bytes from io to fill buf. + fn read_bytes(&mut self) -> Result { + unsafe { + self.buf.set_len(1024); + } + loop { + return match self.io.read(self.buf.as_mut_slice()) { + Ok(0) => unsafe { + self.buf.set_len(0); + Ok(false) + }, + Ok(n) => unsafe { + self.buf.set_len(n); + Ok(true) + }, + Err(ref e) if e.kind() == ErrorKind::WouldBlock => continue, + Err(e) => Err(e), + }; + } + } + + // If there is not enough bytes in buf, try to read some bytes from io and + // reset some parameters inside. + fn load(&mut self) -> Result { + if let Some(ref mut cacher) = self.cache { + cacher.cache.extend_from_slice(&self.buf[cacher.pre..]); + cacher.pre = 0; + } + let result = self.read_bytes(); + if let Ok(true) = result { + self.cur = 0; + } + result + } + + // Every time a user calls a cache-related interface, the cache content + // needs to be updated in time. + fn update_cache(&mut self) { + if let Some(ref mut cacher) = self.cache { + if self.cur > cacher.pre { + cacher + .cache + .extend_from_slice(&self.buf[cacher.pre..self.cur]); + } + cacher.pre = self.cur; + } + } +} + +impl BytesReader for IoReader { + type Error = Error; + + fn next(&mut self) -> Result> { + if self.cur == self.buf.len() { + match self.load() { + Ok(true) => {} + Ok(false) => return Ok(None), + Err(e) => return Err(e), + } + } + + let ch = self.buf[self.cur]; + self.cur += 1; + self.idx += 1; + + if ch == b'\n' { + self.pos.line += 1; + self.pos.column = 1; + } else { + self.pos.column += 1; + } + + Ok(Some(ch)) + } + + fn peek(&mut self) -> Result> { + if self.cur == self.buf.len() { + match self.load() { + Ok(true) => {} + Ok(false) => return Ok(None), + Err(e) => return Err(e), + } + } + + Ok(Some(self.buf[self.cur])) + } + + fn discard(&mut self) { + if self.cur == self.buf.len() { + match self.load() { + Ok(true) => {} + Ok(false) => return, + Err(_) => return, + } + } + + let ch = self.buf[self.cur]; + self.cur += 1; + self.idx += 1; + + if ch == b'\n' { + self.pos.line += 1; + self.pos.column = 1; + } else { + self.pos.column += 1; + } + } + + #[inline] + fn index(&self) -> usize { + self.idx + } + + #[inline] + fn position(&self) -> Position { + self.pos.clone() + } +} + +impl Cacheable for IoReader { + fn start_caching(&mut self) { + if let Some(ref mut cacher) = self.cache { + cacher.cache.clear(); + cacher.pre = self.cur; + } else { + let mut cache = Cache::new(); + cache.pre = self.cur; + self.cache = Some(cache); + } + } + + fn cached_len(&mut self) -> Option { + self.update_cache(); + self.cache.as_ref().map(|c| c.cache.len()) + } + + fn cached_slice(&mut self) -> Option<&[u8]> { + self.update_cache(); + self.cache.as_ref().map(|c| c.cache.as_slice()) + } + + fn cached_data(&mut self) -> Option> { + self.update_cache(); + self.cache.as_ref().map(|c| c.cache.clone()) + } + + fn end_caching(&mut self) { + self.cache = None; + } + + fn take_cached_data(&mut self) -> Option> { + self.update_cache(); + self.cache.take().map(|c| c.cache) + } +} + +#[cfg(test)] +mod ut_io_reader { + use super::{BytesReader, Cacheable, IoReader}; + use std::cmp; + use std::io::{ErrorKind, Read}; + + struct TestIo { + vec: Vec, + idx: usize, + } + + impl TestIo { + fn new(vec: Vec) -> Self { + Self { vec, idx: 0 } + } + } + + impl Read for TestIo { + fn read(&mut self, buf: &mut [u8]) -> std::io::Result { + if self.idx == self.vec.len() { + return Ok(0); + } + let last = cmp::min(self.idx + buf.len(), self.vec.len()); + let len = last - self.idx; + buf[..len].copy_from_slice(&self.vec[self.idx..last]); + self.idx = last; + Ok(len) + } + } + + struct TestWouldBlockIo { + cnt: usize, + } + + impl TestWouldBlockIo { + fn new() -> Self { + Self { cnt: 0 } + } + + fn is_finished(&self) -> bool { + self.cnt == 10 + } + } + + impl Read for TestWouldBlockIo { + fn read(&mut self, _buf: &mut [u8]) -> std::io::Result { + if self.cnt < 10 { + self.cnt += 1; + return Err(ErrorKind::WouldBlock.into()); + } + Ok(0) + } + } + + struct TestErrIo; + + impl TestErrIo { + fn new() -> Self { + Self + } + } + + impl Read for TestErrIo { + fn read(&mut self, _buf: &mut [u8]) -> std::io::Result { + Err(ErrorKind::AddrInUse.into()) + } + } + + /// UT test case for `IoReader::new`. + /// + /// # Title + /// ut_io_reader_new + /// + /// # Brief + /// 1. Call `IoReader::new`. + /// 2. Check that parts of the return value are default values. + #[test] + fn ut_io_reader_new() { + let io = TestIo::new(Vec::new()); + let io_reader = IoReader::new(io); + + assert_eq!(io_reader.cur, 0); + assert_eq!(io_reader.idx, 0); + assert_eq!(io_reader.pos.line, 1); + assert_eq!(io_reader.pos.column, 1); + assert_eq!(io_reader.buf.capacity(), 1024); + assert!(io_reader.buf.is_empty()); + assert!(io_reader.cache.is_none()); + } + + /// UT test case for `IoReader::next`. + /// + /// # Title + /// ut_test_case_io_reader_next + /// + /// # Brief + /// 1. Create a `IoReader`. + /// 2. Call `IoReader::next`. + /// 3. Check the return value against the following conditions: + /// - If the end is not read, it returns `Ok(Some(..))`, and the index + /// is moved backward; if the end is read, it returns `Ok(None)`, and + /// the index is not moved. + #[test] + fn ut_io_reader_next() { + // Use TestIo. + let io = TestIo::new(vec![1u8; 1025]); + let mut io_reader = IoReader::new(io); + assert_eq!(io_reader.next().unwrap(), Some(1)); + for _ in 0..1023 { + let _ = io_reader.next().unwrap(); + } + assert_eq!(io_reader.next().unwrap(), Some(1)); + assert_eq!(io_reader.next().unwrap(), None); + + // Use TestWouldBlockIo. + let io = TestWouldBlockIo::new(); + let mut io_reader = IoReader::new(io); + assert_eq!(io_reader.next().unwrap(), None); + assert!(io_reader.io.is_finished()); + + // Use TestErrIo + let io = TestErrIo::new(); + let mut io_reader = IoReader::new(io); + assert!(io_reader.next().is_err()); + } + + /// UT test case for `IoReader::peek`. + /// + /// # Title + /// ut_io_reader_peek + /// + /// # Brief + /// 1. Create a `IoReader`. + /// 2. Call `IoReader::peek`. + /// 3. Check the return value against the following conditions: + /// - If the end is not read, it returns `Ok(Some(..))`; if the end is + /// read, it returns `Ok(None)`. + #[test] + fn ut_io_reader_peek() { + // Use TestIo. + let io = TestIo::new(vec![1u8; 1]); + let mut io_reader = IoReader::new(io); + assert_eq!(io_reader.peek().unwrap(), Some(1)); + assert_eq!(io_reader.peek().unwrap(), Some(1)); + assert_eq!(io_reader.next().unwrap(), Some(1)); + assert_eq!(io_reader.peek().unwrap(), None); + + // Use TestWouldBlockIo. + let io = TestWouldBlockIo::new(); + let mut io_reader = IoReader::new(io); + assert_eq!(io_reader.peek().unwrap(), None); + assert!(io_reader.io.is_finished()); + + // Use TestErrorIo. + let io = TestErrIo::new(); + let mut io_reader = IoReader::new(io); + assert!(io_reader.peek().is_err()); + } + + /// UT test case for `IoReader::discard`. + /// + /// # Title + /// ut_io_reader_discard + /// + /// # Brief + /// 1. Create a `IoReader`. + /// 2. Call `IoReader::discard`. + /// 3. Check `index` against the following conditions: + /// - If the end is not read, the index is moved backward; if the end is + /// read, the index is not moved. + #[test] + fn ut_io_reader_discard() { + let io = TestIo::new(vec![1u8; 1]); + let mut io_reader = IoReader::new(io); + assert_eq!(io_reader.index(), 0); + io_reader.discard(); + assert_eq!(io_reader.index(), 1); + io_reader.discard(); + assert_eq!(io_reader.index(), 1); + } + + /// UT test case for `IoReader::index`. + /// + /// # Title + /// ut_io_reader_index + /// + /// # Brief + /// 1. Create a `IoReader`. + /// 2. Call `IoReader::index`. + /// 3. Check if the `index` is correct. + #[test] + fn ut_io_reader_index() { + let io = TestIo::new(vec![1u8; 1]); + let io_reader = IoReader::new(io); + assert_eq!(io_reader.index(), 0); + } + + /// UT test case for `IoReader::position`. + /// + /// # Title + /// ut_io_reader_position + /// + /// # Brief + /// 1. Create a `IoReader`. + /// 2. Call `IoReader::position`. + /// 3. Check the return value against the following conditions: + /// - If `'\n'` is read, the line number will increase and the column + /// number will return to 1; if other characters are read, the line + /// number will remain unchanged and the column number will increase. + #[test] + fn ut_io_reader_position() { + let io = TestIo::new(vec![1u8, b'\n', 2, b'\n', 3]); + let mut io_reader = IoReader::new(io); + let position = io_reader.position(); + assert_eq!(position.line(), 1); + assert_eq!(position.column(), 1); + assert_eq!(io_reader.next().unwrap(), Some(1)); + + // Use `next()`. + let position = io_reader.position(); + assert_eq!(position.line(), 1); + assert_eq!(position.column(), 2); + assert_eq!(io_reader.next().unwrap(), Some(b'\n')); + + let position = io_reader.position(); + assert_eq!(position.line(), 2); + assert_eq!(position.column(), 1); + assert_eq!(io_reader.next().unwrap(), Some(2)); + + // Use `peek()` and `discard()`. + let position = io_reader.position(); + assert_eq!(position.line(), 2); + assert_eq!(position.column(), 2); + assert_eq!(io_reader.peek().unwrap(), Some(b'\n')); + io_reader.discard(); + + let position = io_reader.position(); + assert_eq!(position.line(), 3); + assert_eq!(position.column(), 1); + assert_eq!(io_reader.peek().unwrap(), Some(3)); + io_reader.discard(); + + let position = io_reader.position(); + assert_eq!(position.line(), 3); + assert_eq!(position.column(), 2); + assert_eq!(io_reader.peek().unwrap(), None); + } + + /// UT test case for `IoReader::start_caching`. + /// + /// # Title + /// ut_io_reader_start_caching + /// + /// # Brief + /// 1. Create a `IoReader`. + /// 2. Call `IoReader::start_caching`. + /// 3. Check if `cache` is correct. + #[test] + fn ut_io_reader_start_caching() { + let io = TestIo::new(vec![1]); + let mut io_reader = IoReader::new(io); + assert!(io_reader.cache.is_none()); + io_reader.start_caching(); + assert!(io_reader.cache.is_some()); + assert_eq!(io_reader.cached_len(), Some(0)); + + assert_eq!(io_reader.next().unwrap(), Some(1)); + assert_eq!(io_reader.cached_len(), Some(1)); + io_reader.start_caching(); + assert_eq!(io_reader.cached_len(), Some(0)); + } + + /// UT test case for `IoReader::cached_len`. + /// + /// # Title + /// ut_io_reader_cached_len + /// + /// # Brief + /// 1. Create a `IoReader`. + /// 2. Call `IoReader::cached_len`. + /// 3. Check the return value against the following conditions: + /// - Returns `None` if caching is not enabled, otherwise returns + /// `Some(..)`. + #[test] + fn ut_io_reader_cached_len() { + let io = TestIo::new(Vec::new()); + let mut io_reader = IoReader::new(io); + assert_eq!(io_reader.cached_len(), None); + io_reader.start_caching(); + assert_eq!(io_reader.cached_len(), Some(0)); + } + + /// UT test case for `IoReader::cached_slice`. + /// + /// # Title + /// ut_io_reader_cached_slice + /// + /// # Brief + /// 1. Create a `IoReader`. + /// 2. Call `IoReader::cached_slice`. + /// 3. Check the return value against the following conditions: + /// - Returns `None` if caching is not enabled, otherwise returns + /// `Some(..)`. + #[test] + fn ut_io_reader_cached_slice() { + let io = TestIo::new(Vec::new()); + let mut io_reader = IoReader::new(io); + assert_eq!(io_reader.cached_slice(), None); + io_reader.start_caching(); + assert_eq!(io_reader.cached_slice(), Some([].as_slice())); + + // Test 1025 bytes. + let mut input = vec![0; 1024]; + input.push(1); + let io = TestIo::new(input); + let mut io_reader = IoReader::new(io); + for _ in 0..1023 { + let _ = io_reader.next(); + } + io_reader.start_caching(); + assert_eq!(io_reader.next().unwrap(), Some(0)); + assert_eq!(io_reader.next().unwrap(), Some(1)); + assert_eq!(io_reader.cached_len(), Some(2)); + } + + /// UT test case for `IoReader::cached_data`. + /// + /// # Title + /// ut_io_reader_cached_slice + /// + /// # Brief + /// 1. Create a `IoReader`. + /// 2. Call `IoReader::cached_data`. + /// 3. Check the return value against the following conditions: + /// - Returns `None` if caching is not enabled, otherwise returns + /// `Some(..)`. + #[test] + fn ut_io_reader_cached_data() { + let io = TestIo::new(Vec::new()); + let mut io_reader = IoReader::new(io); + assert_eq!(io_reader.cached_data(), None); + io_reader.start_caching(); + assert_eq!(io_reader.cached_data(), Some(Vec::new())); + } + + /// UT test case for `IoReader::end_caching`. + /// + /// # Title + /// ut_io_reader_end_caching + /// + /// # Brief + /// 1. Create a `IoReader`. + /// 2. Call `IoReader::end_caching`. + /// 3. Check if `cache` is correct. + #[test] + fn ut_io_reader_end_caching() { + let io = TestIo::new(Vec::new()); + let mut io_reader = IoReader::new(io); + io_reader.start_caching(); + assert!(io_reader.cache.is_some()); + io_reader.end_caching(); + assert!(io_reader.cache.is_none()); + } + + /// UT test case for `IoReader::take_cached_data`. + /// + /// # Title + /// ut_io_reader_take_cached_data + /// + /// # Brief + /// 1. Create a `IoReader`. + /// 2. Call `IoReader::take_cached_data`. + /// 3. Check if the return value is correct. + #[test] + fn ut_io_reader_take_cached_data() { + let io = TestIo::new(Vec::new()); + let mut io_reader = IoReader::new(io); + io_reader.start_caching(); + assert!(io_reader.cache.is_some()); + assert_eq!(io_reader.take_cached_data(), Some(Vec::new())); + assert!(io_reader.cache.is_none()); + } +} diff --git a/src/reader/mod.rs b/src/reader/mod.rs new file mode 100644 index 0000000000000000000000000000000000000000..325736650101e55566fa2ebe6656ba6fce8c49c5 --- /dev/null +++ b/src/reader/mod.rs @@ -0,0 +1,198 @@ +/* + * Copyright (c) 2023 Huawei Device Co., Ltd. + * Licensed 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. + */ + +mod io_reader; +pub(crate) use io_reader::IoReader; + +mod slice_reader; +pub(crate) use slice_reader::SliceReader; + +/// `BytesReader` provides the basic byte read interface, such as `next`, +/// `peek`, `index`. Users can obtain the next byte or the current read +/// position according to these interfaces. +pub(crate) trait BytesReader { + /// Errors that may occur during reading, usually in the I\O process. + type Error: Into>; + + /// Get the next character and move the cursor to the next place. + fn next(&mut self) -> Result, Self::Error>; + + /// Get the next character, but don't move the cursor. So the next read + /// will get the same character. + fn peek(&mut self) -> Result, Self::Error>; + + /// Discard the next character and move the cursor to the next place. + fn discard(&mut self); + + /// Get the current cursor position and return it as usize. + fn index(&self) -> usize; + + /// Get the current cursor position and return it as `Position`. + fn position(&self) -> Position; +} + +/// `Cacheable` provides some byte cache interfaces for caching a portion of +/// contiguous bytes in a byte stream. +pub(crate) trait Cacheable: BytesReader { + /// Start the cache operation. This interface needs to be used with + /// `end_caching` or `take_cached_data`. + fn start_caching(&mut self); + + /// Get the length of the cached bytes. Since the logic of caching + /// operations is implementation-dependent, we provide an interface that + /// uses mutable references here. + fn cached_len(&mut self) -> Option; + + /// Get a slice of the cached bytes. Since the logic of caching operations + /// is implementation-dependent, we provide an interface that uses mutable + /// references here. + fn cached_slice(&mut self) -> Option<&[u8]>; + + /// Get a `Vec` of the cached bytes. Since the logic of caching operations + /// is implementation-dependent, we provide an interface that uses mutable + /// references here. + fn cached_data(&mut self) -> Option>; + + /// End the cache operation. This interface needs to be used with + /// `start_caching`. + fn end_caching(&mut self); + + /// End the cache operation and return the cached bytes. This interface + /// needs to be used with `start_caching`. + fn take_cached_data(&mut self) -> Option>; +} + +/// `RemainderCountable` provides the interface related to the remainder. +pub(crate) trait RemainderCountable: BytesReader { + /// Get the length of the remainder. + fn remainder_len(&self) -> usize; + + /// Get a slice of the remainder. + fn remainder_slice(&self) -> &[u8]; + + /// Get a `Vec` of the remainder. + fn remainder_data(&self) -> Vec; +} + +/// `NBytesReadable` provides interfaces to read 'n' bytes at one time. +pub(crate) trait NBytesReadable: BytesReader { + /// Read the next 'n' bytes and move the cursor to the next nth position. + /// If there are not enough bytes remaining to satisfy 'n', return `None` + /// and do nothing. + fn next_n(&mut self, n: usize) -> Result, Self::Error>; + + /// Get the next 'n' bytes and do not move the cursor. If there are not + /// enough bytes remaining to satisfy 'n', return `None` and do nothing. + fn peek_n(&mut self, n: usize) -> Result, Self::Error>; + + /// Discard the next 'n' bytes and move the cursor to the next nth position. + /// If there are not enough bytes remaining to satisfy 'n', do nothing. + fn discard_n(&mut self, n: usize); +} + +/// Position information which expressed in row and column. +#[derive(Clone)] +pub(crate) struct Position { + line: usize, + column: usize, +} + +impl Position { + /// Create a `Position` from the given line and column. + #[inline] + pub(crate) fn new(line: usize, column: usize) -> Self { + Self { line, column } + } + + /// Get line. + #[inline] + pub(crate) fn line(&self) -> usize { + self.line + } + + /// Get column. + #[inline] + pub(crate) fn column(&self) -> usize { + self.column + } +} + +#[cfg(test)] +mod ut_position { + use super::Position; + + /// UT test for `Position::new`. + /// + /// # Title + /// ut_position_new + /// + /// # Brief + /// 1. Call `Position::new` to create a `Position`. + /// 2. Check if the results are correct. + #[test] + fn ut_position_new() { + let position = Position::new(1, 1); + assert_eq!(position.line, 1); + assert_eq!(position.column, 1); + } + + /// UT test for `Position::line`. + /// + /// # Title + /// ut_position_line + /// + /// # Brief + /// 1. Create a `Position`. + /// 2. Call `Position::line` to get the line number of `Position`. + /// 3. Check if the results are correct. + #[test] + fn ut_position_line() { + let position = Position::new(1, 1); + assert_eq!(position.line(), 1); + } + + /// UT test for `Position::column`. + /// + /// # Title + /// ut_position_column + /// + /// # Brief + /// 1. Create a `Position`. + /// 2. Call `Position::column` to get the column number of `Position`. + /// 3. Check if the results are correct. + #[test] + fn ut_position_column() { + let position = Position::new(1, 1); + assert_eq!(position.column(), 1); + } + + /// UT test case for `Position::clone`. + /// + /// # Title + /// ut_position_clone + /// + /// # Brief + /// 1. Create a `Position`. + /// 2. Call `Position::clone` to get a copy of `Position`. + /// 3. Check if the results are correct. + #[allow(clippy::redundant_clone)] + #[test] + fn ut_position_clone() { + let position = Position::new(1, 1); + let position = position.clone(); + assert_eq!(position.line, 1); + assert_eq!(position.column, 1); + } +} diff --git a/src/reader/slice_reader.rs b/src/reader/slice_reader.rs new file mode 100644 index 0000000000000000000000000000000000000000..0878c277c00e050288b821c07cf783b7dfb64328 --- /dev/null +++ b/src/reader/slice_reader.rs @@ -0,0 +1,539 @@ +/* + * Copyright (c) 2023 Huawei Device Co., Ltd. + * Licensed 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. + */ + +use super::{BytesReader, Cacheable, NBytesReadable, Position, RemainderCountable}; +use std::convert::Infallible; + +/// Reader for reading slices. This reader implements `BytesReader` trait, +/// `Cacheable` trait and `Countable` trait. +/// +/// let slice = "Hello World"; +/// let mut reader = SliceReader::new(slice.as_bytes()); +/// assert_eq!(reader.next(), Ok(Some(b'H'))); +/// assert_eq!(reader.peek(), Ok(Some(b'e'))); +/// assert_eq!(reader.peek(), Ok(Some(b'e'))); +pub(crate) struct SliceReader<'a> { + slice: &'a [u8], + index: usize, // A cursor to the next character to be read. + cache: Option, // A cache for storing accumulated characters. +} + +// A simple cache implementation for `SliceReader`. We just need to save a +// starting position. +struct Cache(usize); + +impl<'a> SliceReader<'a> { + /// Create a new `SliceReader` from the given slice. + /// + /// let slice = "Hello World"; + /// let reader = SliceReader::new(slice.as_bytes()); + #[inline] + pub(crate) fn new(slice: &'a [u8]) -> Self { + Self { + slice, + index: 0, + cache: None, + } + } +} + +impl<'a> BytesReader for SliceReader<'a> { + type Error = Infallible; // Use Infallible because no error will be returned in SliceReader. + + #[inline] + fn next(&mut self) -> Result, Self::Error> { + if self.index >= self.slice.len() { + return Ok(None); + } + let ch = self.slice[self.index]; + self.index += 1; + Ok(Some(ch)) + } + + #[inline] + fn peek(&mut self) -> Result, Self::Error> { + if self.index >= self.slice.len() { + return Ok(None); + } + Ok(Some(self.slice[self.index])) + } + + #[inline] + fn discard(&mut self) { + if self.index < self.slice.len() { + self.index += 1; + } + } + + #[inline] + fn index(&self) -> usize { + self.index + } + + fn position(&self) -> Position { + // The traversal method is used to calculate the `Position`, which + // is expensive, and it is not recommended to call it frequently. + let index = core::cmp::min(self.index, self.slice.len()); + + let mut position = Position { line: 1, column: 1 }; + for i in 0..index { + match self.slice[i] { + b'\n' => { + position.line += 1; + position.column = 1; + } + _ => { + position.column += 1; + } + } + } + position + } +} + +impl<'a> Cacheable for SliceReader<'a> { + #[inline] + fn start_caching(&mut self) { + self.cache = Some(Cache(self.index)); + } + + #[inline] + fn cached_len(&mut self) -> Option { + self.cache.as_ref().map(|c| self.index - c.0) + } + + #[inline] + fn cached_slice(&mut self) -> Option<&[u8]> { + self.cache.as_ref().map(|c| &self.slice[c.0..self.index]) + } + + #[inline] + fn cached_data(&mut self) -> Option> { + self.cache + .as_ref() + .map(|c| self.slice[c.0..self.index].to_vec()) + } + + #[inline] + fn end_caching(&mut self) { + self.cache = None; + } + + #[inline] + fn take_cached_data(&mut self) -> Option> { + self.cache + .take() + .map(|c| self.slice[c.0..self.index].to_vec()) + } +} + +impl<'a> RemainderCountable for SliceReader<'a> { + #[inline] + fn remainder_len(&self) -> usize { + self.slice.len() - self.index + } + + #[inline] + fn remainder_slice(&self) -> &[u8] { + &self.slice[self.index..] + } + + #[inline] + fn remainder_data(&self) -> Vec { + self.remainder_slice().to_vec() + } +} + +impl<'a> NBytesReadable for SliceReader<'a> { + fn next_n(&mut self, n: usize) -> Result, Self::Error> { + if self.index + n > self.slice.len() { + return Ok(None); + } + let result = &self.slice[self.index..self.index + n]; + self.index += n; + Ok(Some(result)) + } + + fn peek_n(&mut self, n: usize) -> Result, Self::Error> { + if self.index + n > self.slice.len() { + return Ok(None); + } + Ok(Some(&self.slice[self.index..self.index + n])) + } + + fn discard_n(&mut self, n: usize) { + if self.index + n > self.slice.len() { + return; + } + self.index += n; + } +} + +#[cfg(test)] +mod ut_slice_reader { + use super::{BytesReader, Cacheable, NBytesReadable, RemainderCountable, SliceReader}; + + /// UT test case for `SliceReader::new`. + /// + /// # Title + /// ut_slice_reader_new + /// + /// # Brief + /// 1. Call `SliceReader::new`. + /// 2. Check that parts of the return value are default values. + #[test] + fn ut_slice_reader_new() { + let slice = "A"; + let slice_reader = SliceReader::new(slice.as_bytes()); + assert_eq!(slice_reader.slice, slice.as_bytes()); + assert_eq!(slice_reader.index, 0); + assert!(slice_reader.cache.is_none()); + } + + /// UT test case for `SliceReader::next`. + /// + /// # Title + /// ut_slice_reader_next + /// + /// # Brief + /// 1. Create a `SliceReader`. + /// 2. Call `SliceReader::next`. + /// 3. Check the return value against the following conditions: + /// - If the end is not read, it returns `Ok(Some(..))`, and the index + /// is moved backward; if the end is read, it returns `Ok(None)`, and + /// the index is not moved. + #[test] + fn ut_slice_reader_next() { + let slice = "A"; + let mut slice_reader = SliceReader::new(slice.as_bytes()); + assert_eq!(slice_reader.next(), Ok(Some(b'A'))); + assert_eq!(slice_reader.next(), Ok(None)); + } + + /// UT test case for `SliceReader::peek`. + /// + /// # Title + /// ut_slice_reader_peek + /// + /// # Brief + /// 1. Create a `SliceReader`. + /// 2. Call `SliceReader::peek`. + /// 3. Check the return value against the following conditions: + /// - If the end is not read, it returns `Ok(Some(..))`; if the end is + /// read, it returns `Ok(None)`. + #[test] + fn ut_slice_reader_peek() { + let slice = "A"; + let mut slice_reader = SliceReader::new(slice.as_bytes()); + assert_eq!(slice_reader.peek(), Ok(Some(b'A'))); + assert_eq!(slice_reader.peek(), Ok(Some(b'A'))); + assert_eq!(slice_reader.next(), Ok(Some(b'A'))); + assert_eq!(slice_reader.peek(), Ok(None)); + } + + /// UT test case for `SliceReader::discard`. + /// + /// # Title + /// ut_slice_reader_discard + /// + /// # Brief + /// 1. Create a `SliceReader`. + /// 2. Call `SliceReader::discard`. + /// 3. Check `index` against the following conditions: + /// - If the end is not read, the index is moved backward; if the end is + /// read, the index is not moved. + #[test] + fn ut_slice_reader_discard() { + let slice = "A"; + let mut slice_reader = SliceReader::new(slice.as_bytes()); + assert_eq!(slice_reader.index, 0); + slice_reader.discard(); + assert_eq!(slice_reader.index, 1); + slice_reader.discard(); + assert_eq!(slice_reader.index, 1); + } + + /// UT test case for `SliceReader::index`. + /// + /// # Title + /// ut_slice_reader_index + /// + /// # Brief + /// 1. Create a `SliceReader`. + /// 2. Call `SliceReader::index`. + /// 3. Check if the `index` is correct. + #[test] + fn ut_slice_reader_index() { + let slice = "A"; + let slice_reader = SliceReader::new(slice.as_bytes()); + assert_eq!(slice_reader.index(), 0); + } + + /// UT test case for `SliceReader::position`. + /// + /// # Title + /// ut_slice_reader_position + /// + /// # Brief + /// 1. Create a `SliceReader`. + /// 2. Call `SliceReader::position`. + /// 3. Check the return value against the following conditions: + /// - If `'\n'` is read, the line number will increase and the column + /// number will return to 1; if other characters are read, the line + /// number will remain unchanged and the column number will increase. + #[test] + fn ut_slice_reader_position() { + let slice = "A\nB"; + let mut slice_reader = SliceReader::new(slice.as_bytes()); + let position = slice_reader.position(); + assert_eq!(position.line(), 1); + assert_eq!(position.column(), 1); + assert_eq!(slice_reader.next(), Ok(Some(b'A'))); + + let position = slice_reader.position(); + assert_eq!(position.line(), 1); + assert_eq!(position.column(), 2); + assert_eq!(slice_reader.next(), Ok(Some(b'\n'))); + + let position = slice_reader.position(); + assert_eq!(position.line(), 2); + assert_eq!(position.column(), 1); + assert_eq!(slice_reader.next(), Ok(Some(b'B'))); + + let position = slice_reader.position(); + assert_eq!(position.line(), 2); + assert_eq!(position.column(), 2); + + assert_eq!(slice_reader.next(), Ok(None)); + let position = slice_reader.position(); + assert_eq!(position.line(), 2); + assert_eq!(position.column(), 2); + } + + /// UT test case for `SliceReader::start_caching`. + /// + /// # Title + /// ut_slice_reader_start_caching + /// + /// # Brief + /// 1. Create a `SliceReader`. + /// 2. Call `SliceReader::start_caching`. + /// 3. Check if `cache` is correct. + #[test] + fn ut_slice_reader_start_caching() { + let slice = "A"; + let mut slice_reader = SliceReader::new(slice.as_bytes()); + assert!(slice_reader.cache.is_none()); + slice_reader.start_caching(); + assert_eq!(slice_reader.cache.as_ref().unwrap().0, 0); + } + + /// UT test case for `SliceReader::cached_len`. + /// + /// # Title + /// ut_slice_reader_cached_len + /// + /// # Brief + /// 1. Create a `SliceReader`. + /// 2. Call `SliceReader::cached_len`. + /// 3. Check the return value against the following conditions: + /// - Returns `None` if caching is not enabled, otherwise returns + /// `Some(..)`. + #[test] + fn ut_slice_reader_cached_len() { + let slice = "A"; + let mut slice_reader = SliceReader::new(slice.as_bytes()); + assert_eq!(slice_reader.cached_len(), None); + slice_reader.start_caching(); + assert_eq!(slice_reader.cached_len(), Some(0)); + } + + /// UT test case for `SliceReader::cached_slice`. + /// + /// # Title + /// ut_slice_reader_cached_slice + /// + /// # Brief + /// 1. Create a `SliceReader`. + /// 2. Call `SliceReader::cached_slice`. + /// 3. Check the return value against the following conditions: + /// - Returns `None` if caching is not enabled, otherwise returns + /// `Some(..)`. + #[test] + fn ut_slice_reader_cached_slice() { + let slice = "A"; + let mut slice_reader = SliceReader::new(slice.as_bytes()); + assert_eq!(slice_reader.cached_slice(), None); + slice_reader.start_caching(); + assert_eq!(slice_reader.cached_slice(), Some([].as_slice())); + } + + /// UT test case for `SliceReader::cached_data`. + /// + /// # Title + /// ut_slice_reader_cached_slice + /// + /// # Brief + /// 1. Create a `SliceReader`. + /// 2. Call `SliceReader::cached_data`. + /// 3. Check the return value against the following conditions: + /// - Returns `None` if caching is not enabled, otherwise returns + /// `Some(..)`. + #[test] + fn ut_slice_reader_cached_data() { + let slice = "A"; + let mut slice_reader = SliceReader::new(slice.as_bytes()); + assert_eq!(slice_reader.cached_data(), None); + slice_reader.start_caching(); + assert_eq!(slice_reader.cached_data(), Some(Vec::new())); + } + + /// UT test case for `SliceReader::end_caching`. + /// + /// # Title + /// ut_slice_reader_end_caching + /// + /// # Brief + /// 1. Create a `SliceReader`. + /// 2. Call `SliceReader::end_caching`. + /// 3. Check if `cache` is correct. + #[test] + fn ut_slice_reader_end_caching() { + let slice = "A"; + let mut slice_reader = SliceReader::new(slice.as_bytes()); + slice_reader.start_caching(); + assert!(slice_reader.cache.is_some()); + slice_reader.end_caching(); + assert!(slice_reader.cache.is_none()); + } + + /// UT test case for `SliceReader::take_cached_data`. + /// + /// # Title + /// ut_slice_reader_take_cached_data + /// + /// # Brief + /// 1. Create a `SliceReader`. + /// 2. Call `SliceReader::take_cached_data`. + /// 3. Check if the return value is correct. + #[test] + fn ut_slice_reader_take_cached_data() { + let slice = "A"; + let mut slice_reader = SliceReader::new(slice.as_bytes()); + slice_reader.start_caching(); + assert!(slice_reader.cache.is_some()); + assert_eq!(slice_reader.take_cached_data(), Some(Vec::new())); + assert!(slice_reader.cache.is_none()); + } + + /// UT test case for `SliceReader::remainder_len`. + /// + /// # Title + /// ut_slice_reader_remainder_len + /// + /// # Brief + /// 1. Create a `SliceReader`. + /// 2. Call `SliceReader::remainder_len`. + /// 3. Check if the return value is correct. + #[test] + fn ut_slice_reader_remainder_len() { + let slice = "A"; + let slice_reader = SliceReader::new(slice.as_bytes()); + assert_eq!(slice_reader.remainder_len(), 1); + } + + /// UT test case for `SliceReader::remainder_slice`. + /// + /// # Title + /// ut_slice_reader_remainder_slice + /// + /// # Brief + /// 1. Create a `SliceReader`. + /// 2. Call `SliceReader::remainder_slice`. + /// 3. Check if the return value is correct. + #[test] + fn ut_slice_reader_remainder_slice() { + let slice = "A"; + let slice_reader = SliceReader::new(slice.as_bytes()); + assert_eq!(slice_reader.remainder_slice(), slice.as_bytes()); + } + + /// UT test case for `SliceReader::remainder_data`. + /// + /// # Title + /// ut_slice_reader_remainder_slice + /// + /// # Brief + /// 1. Create a `SliceReader`. + /// 2. Call `SliceReader::remainder_slice`. + /// 3. Check if the return value is correct. + #[test] + fn ut_slice_reader_remainder_data() { + let slice = "A"; + let slice_reader = SliceReader::new(slice.as_bytes()); + assert_eq!(slice_reader.remainder_data(), slice.as_bytes().to_vec()); + } + + /// UT test case for `SliceReader::next_n`. + /// + /// # Title + /// ut_slice_reader_next_n + /// + /// # Brief + /// 1. Create a `SliceReader`. + /// 2. Call `SliceReader::next_n`. + /// 3. Check if the return value is correct. + #[test] + fn ut_slice_reader_next_n() { + let slice = "ABC"; + let mut slice_reader = SliceReader::new(slice.as_bytes()); + assert_eq!(slice_reader.next_n(2).unwrap(), Some("AB".as_bytes())); + assert_eq!(slice_reader.next_n(2).unwrap(), None); + } + + /// UT test case for `SliceReader::peek_n`. + /// + /// # Title + /// ut_slice_reader_peek_n + /// + /// # Brief + /// 1. Create a `SliceReader`. + /// 2. Call `SliceReader::peek_n`. + /// 3. Check if the return value is correct. + #[test] + fn ut_slice_reader_peek_n() { + let slice = "ABC"; + let mut slice_reader = SliceReader::new(slice.as_bytes()); + assert_eq!(slice_reader.peek_n(2).unwrap(), Some("AB".as_bytes())); + assert_eq!(slice_reader.peek_n(4).unwrap(), None); + } + + /// UT test case for `SliceReader::discard_n`. + /// + /// # Title + /// ut_slice_reader_discard_n + /// + /// # Brief + /// 1. Create a `SliceReader`. + /// 2. Call `SliceReader::discard_n`. + /// 3. Check if the return value is correct. + #[test] + fn ut_slice_reader_discard_n() { + let slice = "ABC"; + let mut slice_reader = SliceReader::new(slice.as_bytes()); + slice_reader.discard_n(2); + assert_eq!(slice_reader.next().unwrap(), Some(b'C')); + } +} diff --git a/src/serializer_compact.rs b/src/serializer_compact.rs new file mode 100644 index 0000000000000000000000000000000000000000..d5d31e6596b080099376a0630875c3909d243b0d --- /dev/null +++ b/src/serializer_compact.rs @@ -0,0 +1,712 @@ +/* + * Copyright (c) 2023 Huawei Device Co., Ltd. + * Licensed 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. + */ + +use crate::encoder::encode_string_inner; +use crate::{Error, Error::*}; +use serde::{ser, ser::SerializeSeq, Serialize}; + +/// A data format that can serialize any data structure supported by Serde. +struct Serializer +where + W: std::io::Write, +{ + writer: W, + element_num: Vec, // Used to record the number of traveled elements in the sequence. +} + +/// An auxiliary struct which implements Write trait used in 'to_string' function. +struct AuxiliaryWriter { + output: Vec, +} + +impl std::io::Write for AuxiliaryWriter { + fn write(&mut self, buf: &[u8]) -> std::io::Result { + self.output.extend_from_slice(buf); + Ok(buf.len()) + } + + fn flush(&mut self) -> std::io::Result<()> { + Ok(()) + } +} + +/// ylong_json::serializer_compact supports two functions to produce as output: 'to_string' and 'to_writer'. +/// +/// The to_string function serialize an instance which implements the Serialize Trait to a string and return. +pub fn to_string(value: &T) -> Result +where + T: Serialize, +{ + let mut writer = AuxiliaryWriter { output: Vec::new() }; + to_writer(value, &mut writer)?; + Ok(unsafe { String::from_utf8_unchecked(writer.output) }) +} + +/// The to_writer function serialize an instance which implements the Serialize Trait and +/// writes result into the writer passed in by the user, which needs to implement the std::io::Write. +pub fn to_writer(value: &T, writer: &mut W) -> Result<(), Error> +where + T: Serialize, + W: std::io::Write, +{ + let mut serializer = Serializer { + writer, + element_num: Vec::new(), + }; + value.serialize(&mut serializer)?; + Ok(()) +} + +impl<'a, W: std::io::Write> ser::Serializer for &'a mut Serializer { + // Using `Ok` to propagate the data structure around simplifies Serializers + // which build in-memory data structures. Set 'ok=()' and write the serialization + // result to the buffer contained by the instance. + type Ok = (); + + // The error type when serializing an instance may occur. + type Error = Error; + + // Associative type composite data structures, such as sequences and maps, + // used to track other states at serialization time. In this case, no state + // is required other than what is already stored in the Serializer struct. + type SerializeSeq = Self; + type SerializeMap = Self; + type SerializeStruct = Self; + type SerializeStructVariant = Self; + type SerializeTuple = Self; + type SerializeTupleStruct = Self; + type SerializeTupleVariant = Self; + + // The following 12 methods take one of the base types of the data model + // and map it to JSON by appending it to the output string. + fn serialize_bool(self, v: bool) -> Result<(), Error> { + self.writer.write_fmt(format_args!("{v}"))?; + Ok(()) + } + + // JSON does not distinguish between integers of different sizes, + // so all signed integers and all unsigned integers will be serialized in the same way. + // Therefore, all integers are converted to 64-bit integers and serialized here. + fn serialize_i8(self, v: i8) -> Result<(), Error> { + self.serialize_i64(i64::from(v)) + } + + fn serialize_i16(self, v: i16) -> Result<(), Error> { + self.serialize_i64(i64::from(v)) + } + + fn serialize_i32(self, v: i32) -> Result<(), Error> { + self.serialize_i64(i64::from(v)) + } + + fn serialize_i64(self, v: i64) -> Result<(), Error> { + self.writer.write_fmt(format_args!("{v}"))?; + Ok(()) + } + + fn serialize_u8(self, v: u8) -> Result<(), Error> { + self.serialize_u64(u64::from(v)) + } + + fn serialize_u16(self, v: u16) -> Result<(), Error> { + self.serialize_u64(u64::from(v)) + } + + fn serialize_u32(self, v: u32) -> Result<(), Error> { + self.serialize_u64(u64::from(v)) + } + + fn serialize_u64(self, v: u64) -> Result<(), Error> { + self.writer.write_fmt(format_args!("{v}"))?; + Ok(()) + } + + // Same way for floating-point types. + fn serialize_f32(self, v: f32) -> Result<(), Error> { + self.serialize_f64(f64::from(v)) + } + + fn serialize_f64(self, v: f64) -> Result<(), Error> { + self.writer.write_fmt(format_args!("{v:?}"))?; + Ok(()) + } + + fn serialize_char(self, v: char) -> Result<(), Error> { + self.serialize_str(v.encode_utf8(&mut [0; 4])) + } + + fn serialize_str(self, v: &str) -> Result<(), Error> { + self.writer.write_all(b"\"")?; + encode_string_inner(&mut self.writer, v)?; + self.writer.write_all(b"\"")?; + Ok(()) + } + + // Serialize a byte array into an array of bytes. + // Binary formats will typically represent byte arrays more compactly. + fn serialize_bytes(self, v: &[u8]) -> Result<(), Error> { + let mut seq = self.serialize_seq(Some(v.len()))?; + for byte in v { + seq.serialize_element(byte)?; + } + seq.end() + } + + // JSON `null` represent an absent optional. + fn serialize_none(self) -> Result<(), Error> { + self.serialize_unit() + } + + // Represent an optional instance as the contained value. + fn serialize_some(self, value: &T) -> Result<(), Error> + where + T: ?Sized + Serialize, + { + value.serialize(self) + } + + // In Serde, unit represents an anonymous value that does not contain any data. + // Map this to JSON as "null". + fn serialize_unit(self) -> Result<(), Error> { + self.writer.write_all(b"null")?; + Ok(()) + } + + // Unit struct represents a named value that does not contain any data. Map it to JSON as "null". + fn serialize_unit_struct(self, _name: &'static str) -> Result<(), Error> { + self.serialize_unit() + } + + // When serializing a unit variant (or any other kind of variant), formats + // can choose whether to track it by index or by name. Binary formats + // usually use index while human-readable formats usually use name. + fn serialize_unit_variant( + self, + _name: &'static str, + _variant_index: u32, + variant: &'static str, // The name of the variant. + ) -> Result<(), Error> { + self.serialize_str(variant) + } + + // Treat newtype structs as insignificant wrappers for the data they contain. + fn serialize_newtype_struct(self, _name: &'static str, value: &T) -> Result<(), Error> + where + T: ?Sized + Serialize, + { + value.serialize(self) + } + + // The newtype variant (and all other variant serialization methods) + // specifically refers to the enumerated representation of the "externally tagged". + // + // Serialize it to JSON with an outer tag of the form '{NAME: VALUE}'. + fn serialize_newtype_variant( + self, + _name: &'static str, + _variant_index: u32, + variant: &'static str, // The name of the variant. + value: &T, + ) -> Result<(), Error> + where + T: ?Sized + Serialize, + { + self.writer.write_all(b"{")?; + variant.serialize(&mut *self)?; + self.writer.write_all(b":")?; + value.serialize(&mut *self)?; + self.writer.write_all(b"}")?; + Ok(()) + } + + // The following is the serialization of compound types. + // The start of the sequence, each value, and the end use three separate method calls. + // The serializer can be able to support sequences for which the length is unknown up front. + // + // This one used to serialize the start of the sequence, which in JSON is `[`. + fn serialize_seq(self, _len: Option) -> Result { + self.element_num.push(0); // Now there is no element in this sequence. + self.writer.write_all(b"[")?; + Ok(self) + } + + // Tuples and sequences are represented similarly in JSON. + // Some formats can represent tuples more efficiently by omitting the length. + fn serialize_tuple(self, len: usize) -> Result { + self.serialize_seq(Some(len)) + } + + // Tuple and sequences are represented similarly in JSON. + fn serialize_tuple_struct( + self, + _name: &'static str, + len: usize, // The number of data fields that will be serialized. + ) -> Result { + self.serialize_seq(Some(len)) + } + + // Tuple variants are represented in JSON as `{ NAME: [DATA...] }`. + // This method used only for the externally tagged representation. + fn serialize_tuple_variant( + self, + _name: &'static str, + _variant_index: u32, + variant: &'static str, // The name of the variant. + _len: usize, + ) -> Result { + self.element_num.push(0); + self.writer.write_all(b"{")?; + variant.serialize(&mut *self)?; + self.writer.write_all(b":[")?; + Ok(self) + } + + // Maps are represented in JSON as `{ K: V, K: V, ... }`. + fn serialize_map(self, _len: Option) -> Result { + self.element_num.push(0); + self.writer.write_all(b"{")?; + Ok(self) + } + + // Structs and maps are represented similarly in JSON. + fn serialize_struct( + self, + _name: &'static str, + len: usize, // The number of data fields that will be serialized. + ) -> Result { + self.serialize_map(Some(len)) + } + + // Struct variants are represented in JSON as `{ NAME: { K: V, ... } }`. + // This used for the externally tagged representation. + fn serialize_struct_variant( + self, + _name: &'static str, + _variant_index: u32, + variant: &'static str, // The name of the variant. + _len: usize, + ) -> Result { + self.element_num.push(0); + self.writer.write_all(b"{")?; + variant.serialize(&mut *self)?; + self.writer.write_all(b":{")?; + Ok(self) + } +} + +impl Serializer +where + W: std::io::Write, +{ + // Serialize a single member of sequence or map. + fn whether_to_add_comma(&mut self) -> Result<(), Error> { + match self.element_num.last_mut() { + None => return Err(IncorrectSerdeUsage), + Some(x) => { + // This is not the first element, add a comma before the element. + if *x > 0 { + self.writer.write_all(b",")? + }; + *x += 1; + } + } + Ok(()) + } + + // Add another half of the parentheses at the end. + fn add_another_half(&mut self, buf: &[u8]) -> Result<(), Error> { + self.writer.write_all(buf)?; + Ok(()) + } +} + +// The following 7 impls used to serialize compound types like sequences and maps. +// At the beginning of serializing such types, one call of Serializer method. +// Next, zero or more calls which serializes a single element of the compound type. +// Finally, one call to end the serializing of the compound type. +// +// This impl is SerializeSeq so these methods are called after calling 'serialize_seq' on Serializer. +impl<'a, W: std::io::Write> ser::SerializeSeq for &'a mut Serializer { + // Must match the `Ok` type of the serializer. + type Ok = (); + // Must match the `Error` type of the serializer. + type Error = Error; + + // Serialize a single element in the sequence. + fn serialize_element(&mut self, value: &T) -> Result<(), Error> + where + T: ?Sized + Serialize, + { + self.whether_to_add_comma()?; + value.serialize(&mut **self) + } + + // Close the sequence. + fn end(self) -> Result<(), Error> { + self.element_num.pop(); + self.add_another_half(b"]") + } +} + +// Same thing but for tuples. +impl<'a, W: std::io::Write> ser::SerializeTuple for &'a mut Serializer { + type Ok = (); + type Error = Error; + + fn serialize_element(&mut self, value: &T) -> Result<(), Error> + where + T: ?Sized + Serialize, + { + self.whether_to_add_comma()?; + value.serialize(&mut **self) + } + + fn end(self) -> Result<(), Error> { + self.element_num.pop(); + self.add_another_half(b"]") + } +} + +// Same thing but for tuple structs. +impl<'a, W: std::io::Write> ser::SerializeTupleStruct for &'a mut Serializer { + type Ok = (); + type Error = Error; + + fn serialize_field(&mut self, value: &T) -> Result<(), Error> + where + T: ?Sized + Serialize, + { + self.whether_to_add_comma()?; + value.serialize(&mut **self) + } + + fn end(self) -> Result<(), Error> { + self.element_num.pop(); + self.add_another_half(b"]") + } +} + +// The tuple variants are slightly different. Refer back to the +// `serialize_tuple_variant` method above: +// +// self.writer.write_all(b"{"); +// variant.serialize(&mut *self)?; +// self.writer.write_all(b":["); +// +// So the `end` method in this impl is responsible for closing +// both the `]` and the `}`. +impl<'a, W: std::io::Write> ser::SerializeTupleVariant for &'a mut Serializer { + type Ok = (); + type Error = Error; + + fn serialize_field(&mut self, value: &T) -> Result<(), Error> + where + T: ?Sized + Serialize, + { + self.whether_to_add_comma()?; + value.serialize(&mut **self) + } + + fn end(self) -> Result<(), Error> { + self.element_num.pop(); + self.add_another_half(b"]}") + } +} + +// Some "Serialize" types cannot hold both keys and values +// in memory, so the "SerializeMap" implementation needs to +// support "serialize_key" and "serialize_value" respectively. +// +// There is a third optional method on the `SerializeMap` trait. +// The 'serialize_entry' method allows the serializer to be optimized +// for cases where both keys and values are available. In JSON, it doesn't +// make a difference so the default behavior for `serialize_entry` is fine. +impl<'a, W: std::io::Write> ser::SerializeMap for &'a mut Serializer { + type Ok = (); + type Error = Error; + + // The Serde data model allows map keys to be any serializable type. JSON + // only allows string keys, so if the key is serialized to something other than a string, + // the following implementation will produce invalid JSON. + fn serialize_key(&mut self, key: &T) -> Result<(), Error> + where + T: ?Sized + Serialize, + { + self.whether_to_add_comma()?; + key.serialize(&mut **self) + } + + // It makes no difference whether the colon is printed at + // the end of 'serialize_key' or at the beginning of 'serialize_value'. + // Here we choose to print at the beginning of 'serialize_value'. + fn serialize_value(&mut self, value: &T) -> Result<(), Error> + where + T: ?Sized + Serialize, + { + self.writer.write_all(b":")?; + value.serialize(&mut **self) + } + + fn end(self) -> Result<(), Error> { + self.add_another_half(b"}") + } +} + +// A structure is like a map where the keys are restricted to a compile-time constant string. +impl<'a, W: std::io::Write> ser::SerializeStruct for &'a mut Serializer { + type Ok = (); + type Error = Error; + + fn serialize_field(&mut self, key: &'static str, value: &T) -> Result<(), Error> + where + T: ?Sized + Serialize, + { + self.whether_to_add_comma()?; + key.serialize(&mut **self)?; + self.writer.write_all(b":")?; + value.serialize(&mut **self) + } + + fn end(self) -> Result<(), Error> { + self.element_num.pop(); + self.add_another_half(b"}") + } +} + +// Similar to 'SerializeTupleVariant', the 'end' method here is responsible for +// closing two curly braces opened by 'serialize_struct_variant'. +impl<'a, W: std::io::Write> ser::SerializeStructVariant for &'a mut Serializer { + type Ok = (); + type Error = Error; + + fn serialize_field(&mut self, key: &'static str, value: &T) -> Result<(), Error> + where + T: ?Sized + Serialize, + { + self.whether_to_add_comma()?; + key.serialize(&mut **self)?; + self.writer.write_all(b":")?; + value.serialize(&mut **self) + } + + fn end(self) -> Result<(), Error> { + self.element_num.pop(); + self.add_another_half(b"}}") + } +} + +#[cfg(test)] +mod ut_serializer { + use super::*; + use std::collections::HashMap; + + /// UT test to serialize simple types. + /// + /// # Title + /// ut_serialize_simple + /// + /// # Brief + /// 1.Uses Serializer::to_string method to serialize simple types. + /// 2.Checks if the test results are correct. + #[test] + fn ut_serialize_simple() { + let value: Option = None; + let expected = "null"; + assert_eq!(to_string(&value).unwrap(), expected); + + let value: Option = Some(123); + let expected = "123"; + assert_eq!(to_string(&value).unwrap(), expected); + + let value = true; + let expected = "true"; + assert_eq!(to_string(&value).unwrap(), expected); + + let value: i8 = 123; + let expected = "123"; + assert_eq!(to_string(&value).unwrap(), expected); + + let value: i16 = 123; + let expected = "123"; + assert_eq!(to_string(&value).unwrap(), expected); + + let value: i32 = 123; + let expected = "123"; + assert_eq!(to_string(&value).unwrap(), expected); + + let value: i64 = 123; + let expected = "123"; + assert_eq!(to_string(&value).unwrap(), expected); + + let value: u8 = 123; + let expected = "123"; + assert_eq!(to_string(&value).unwrap(), expected); + + let value: u16 = 123; + let expected = "123"; + assert_eq!(to_string(&value).unwrap(), expected); + + let value: u32 = 123; + let expected = "123"; + assert_eq!(to_string(&value).unwrap(), expected); + + let value: u64 = 123; + let expected = "123"; + assert_eq!(to_string(&value).unwrap(), expected); + + let value: f32 = 1.0; + let expected = "1.0"; + assert_eq!(to_string(&value).unwrap(), expected); + + let value: f64 = 1.0; + let expected = "1.0"; + assert_eq!(to_string(&value).unwrap(), expected); + + let value = "abc"; + let expected = "\"abc\""; + assert_eq!(to_string(&value).unwrap(), expected); + + let value = b"abc"; + let expected = "[97,98,99]"; + assert_eq!(to_string(&value).unwrap(), expected); + } + + /// UT test to serialize struct + /// + /// # Title + /// ut_serialize_struct + /// + /// # Brief + /// 1.Uses Serializer::to_string method to serialize struct. + /// 2.Checks if the test results are correct. + #[test] + fn ut_serialize_struct() { + #[derive(Serialize)] + struct TestUnit; + let test = TestUnit; + let expected = "null"; + assert_eq!(to_string(&test).unwrap(), expected); + + #[derive(Serialize)] + struct TestNewtype(u32); + let test = TestNewtype(123); + let expected = "123"; + assert_eq!(to_string(&test).unwrap(), expected); + + #[derive(Serialize)] + struct TestTuple(u32, u32, bool); + let test = TestTuple(123, 321, true); + let expected = "[123,321,true]"; + assert_eq!(to_string(&test).unwrap(), expected); + + #[derive(Serialize)] + struct Test { + int: u32, + seq: Vec<&'static str>, + tup: (i32, i32, i32), + } + + let test = Test { + int: 1, + seq: vec!["a", "b"], + tup: (1, 2, 3), + }; + let expected = r#"{"int":1,"seq":["a","b"],"tup":[1,2,3]}"#; + assert_eq!(to_string(&test).unwrap(), expected); + } + + /// UT test to serialize enum + /// + /// # Title + /// ut_serialize_enum + /// + /// # Brief + /// 1.Uses Serializer::to_string method to serialize enum. + /// 2.Checks if the test results are correct. + #[test] + fn ut_serialize_enum() { + #[derive(Serialize)] + enum E { + Newtype(i32), + Unit, + Struct { a: u32 }, + Tuple(u32, u32), + } + + let n = E::Newtype(-1); + let expected = r#"{"Newtype":-1}"#; + assert_eq!(to_string(&n).unwrap(), expected); + + let u = E::Unit; + let expected = r#""Unit""#; + assert_eq!(to_string(&u).unwrap(), expected); + + let s = E::Struct { a: 10 }; + let expected = r#"{"Struct":{"a":10}}"#; + assert_eq!(to_string(&s).unwrap(), expected); + + let t = E::Tuple(100, 200); + let expected = r#"{"Tuple":[100,200]}"#; + assert_eq!(to_string(&t).unwrap(), expected); + } + + /// UT test to serialize string + /// + /// # Title + /// ut_serialize_string + /// + /// # Brief + /// 1.Uses Serializer::to_string method to serialize string. + /// 2.Checks if the test results are correct. + #[test] + fn ut_serialize_string() { + let ch = 't'; + let expected = r#""t""#; + assert_eq!(to_string(&ch).unwrap(), expected); + + let value = String::from("test string."); + let expected = r#""test string.""#; + assert_eq!(to_string(&value).unwrap(), expected); + + #[cfg(not(feature = "ascii_only"))] + { + let ch = '中'; + let expected = r#""\u4e2d""#; + assert_eq!(to_string(&ch).unwrap(), expected); + + let value = String::from("中文测试字符串"); + let expected = r#""\u4e2d\u6587\u6d4b\u8bd5\u5b57\u7b26\u4e32""#; + assert_eq!(to_string(&value).unwrap(), expected); + } + } + + /// UT test to serializer object + /// + /// # Title + /// ut_serialize_object + /// + /// # Brief + /// 1.Uses Serializer::to_string method to serialize object. + /// 2.Checks if the test results are correct. + #[test] + fn ut_serialize_object() { + let mut hash = HashMap::new(); + hash.insert("apple", 1); + let expected = r#"{"apple":1}"#; + assert_eq!(to_string(&hash).unwrap(), expected); + hash.insert("banana", 2); + assert!(to_string(&hash).is_ok()); + } +} diff --git a/src/states.rs b/src/states.rs new file mode 100644 index 0000000000000000000000000000000000000000..166d1088c80a46e99107b74e65c2c20540c19460 --- /dev/null +++ b/src/states.rs @@ -0,0 +1,1117 @@ +/* + * Copyright (c) 2023 Huawei Device Co., Ltd. + * Licensed 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. + */ + +use crate::reader::Cacheable; +use crate::{ + consts::*, deserializer::Deserializer, Array, Error, JsonValue, Number, Object, ParseError, +}; +use core::convert::TryFrom; +#[cfg(feature = "c_adapter")] +pub use std::ffi::CString; + +macro_rules! unexpected_character { + ($deserializer: expr) => {{ + let position = $deserializer.reader.position(); + match read_error_char($deserializer) { + Ok(Some(ch)) => { + Err(ParseError::UnexpectedCharacter(position.line(), position.column(), ch).into()) + } + Ok(None) => Err(ParseError::InvalidUtf8Bytes(position.line()).into()), + Err(e) => Err(e), + } + }}; +} + +macro_rules! unexpected_eoj { + ($deserializer: expr) => { + Err(ParseError::UnexpectedEndOfJson($deserializer.reader.position().line()).into()) + }; +} + +macro_rules! eat_whitespace_until_not { + ($deserializer: expr) => {{ + loop { + match $deserializer.reader.peek().map_err(Error::new_reader)? { + Some(ch) if WHITE_SPACE_SET.contains(&ch) => $deserializer.reader.discard(), + x => break x, + } + } + }}; +} + +macro_rules! eat_digits_until_not { + ($deserializer: expr) => {{ + loop { + match $deserializer.reader.peek().map_err(Error::new_reader)? { + Some(ch) if (ZERO..=NINE).contains(&ch) => $deserializer.reader.discard(), + x => break x, + } + } + }}; +} + +macro_rules! match_str { + ($deserializer: expr, $str: expr) => {{ + for item in $str { + match $deserializer.reader.peek().map_err(Error::new_reader)? { + Some(ch) if ch == *item => $deserializer.reader.discard(), + Some(_) => return unexpected_character!($deserializer), + None => return unexpected_eoj!($deserializer), + } + } + }}; +} + +pub(crate) fn check_recursion( + deserializer: &mut Deserializer, +) -> Result<(), Error> { + if deserializer.recursion_depth > RECURSION_LIMIT { + Err(Error::ExceedRecursionLimit) + } else { + Ok(()) + } +} + +#[inline] +pub(crate) fn start_parsing( + deserializer: &mut Deserializer, +) -> Result { + let value = parse_value(deserializer)?; + + // If the text is not finished, return TrailingBytes Error. + if eat_whitespace_until_not!(deserializer).is_some() { + return Err(ParseError::TrailingBytes(deserializer.reader.position().line()).into()); + } + Ok(value) +} + +// Parses value. +fn parse_value(deserializer: &mut Deserializer) -> Result { + match eat_whitespace_until_not!(deserializer) { + Some(ZERO..=NINE | MINUS) => Ok(JsonValue::Number(parse_number(deserializer)?)), + Some(LEFT_CURLY_BRACKET) => { + deserializer.reader.discard(); + parse_object(deserializer) + } + Some(LEFT_SQUARE_BRACKET) => { + deserializer.reader.discard(); + parse_array(deserializer) + } + Some(QUOTATION_MARK) => { + deserializer.reader.discard(); + Ok(JsonValue::String(parse_string(deserializer)?)) + } + Some(T_LOWER) => { + deserializer.reader.discard(); + match_str!(deserializer, TRUE_LEFT_STR); + Ok(JsonValue::Boolean(true)) + } + Some(F_LOWER) => { + deserializer.reader.discard(); + match_str!(deserializer, FALSE_LEFT_STR); + Ok(JsonValue::Boolean(false)) + } + Some(N_LOWER) => { + deserializer.reader.discard(); + match_str!(deserializer, NULL_LEFT_STR); + Ok(JsonValue::Null) + } + Some(_) => unexpected_character!(deserializer), + None => unexpected_eoj!(deserializer), + } +} + +// Parses object +fn parse_object(deserializer: &mut Deserializer) -> Result { + // Uses an internal state machine to determine the flow. + enum InnerState { + Start, // State at the start of the match. + AfterComma, // Comma already exists. + NoComma, // Comma didn't exist before + } + + deserializer.recursion_depth += 1; + check_recursion(deserializer)?; + + // Creates an Object to store key-value pairs. + let mut object = Object::new(); + // The initial status is Start. + let mut state = InnerState::Start; + + loop { + match (state, eat_whitespace_until_not!(deserializer)) { + // If "}" is encountered in the initial or NoComma state, object is null. + (InnerState::Start | InnerState::NoComma, Some(RIGHT_CURLY_BRACKET)) => { + deserializer.reader.discard(); + deserializer.recursion_depth -= 1; + break; + } + // If "\" is encountered in the initial state or + // if "," is already present, matches key-value pairs. + (InnerState::Start | InnerState::AfterComma, Some(QUOTATION_MARK)) => { + deserializer.reader.discard(); + let k = parse_key(deserializer)?; + + // Matches ':' + match eat_whitespace_until_not!(deserializer) { + Some(COLON) => deserializer.reader.discard(), + Some(_) => return unexpected_character!(deserializer), + None => return unexpected_eoj!(deserializer), + }; + + // Inserts into object. + object.insert(k, parse_value(deserializer)?); + + // Sets the state to NoComma. + state = InnerState::NoComma; + } + // In the initial state, it is illegal to encounter any other character. + (InnerState::Start, Some(_)) => return unexpected_character!(deserializer), + // In the NoComma state, when "," is encountered, converts state to HaveComma. + (InnerState::NoComma, Some(COMMA)) => { + deserializer.reader.discard(); + state = InnerState::AfterComma; + } + // In the NoComma state, it's illegal to encounter any other character. + (InnerState::NoComma, Some(_)) => return unexpected_character!(deserializer), + // In the HaveComma state, it's illegal to encounter any other character. + (InnerState::AfterComma, Some(_)) => return unexpected_character!(deserializer), + // In all cases, None is illegal. + (_, None) => return unexpected_eoj!(deserializer), + } + } + Ok(JsonValue::Object(object)) +} + +// Parses string +#[cfg(not(feature = "c_adapter"))] +pub(crate) fn parse_string( + deserializer: &mut Deserializer, +) -> Result { + let vec = parse_string_inner(deserializer)?; + // Since the vec contents are all checked upon matching, the unchecked method is used directly here. + Ok(unsafe { String::from_utf8_unchecked(vec) }) +} + +#[cfg(feature = "c_adapter")] +pub(crate) fn parse_string( + deserializer: &mut Deserializer, +) -> Result { + let vec = parse_string_inner(deserializer)?; + // Since the vec contents are all checked upon matching, the unchecked method is used directly here. + Ok(unsafe { CString::from_vec_unchecked(vec) }) +} + +// Parses key +#[inline] +fn parse_key(deserializer: &mut Deserializer) -> Result { + let vec = parse_string_inner(deserializer)?; + // Since the vec contents are all checked upon matching, the unchecked method is used directly here. + Ok(unsafe { String::from_utf8_unchecked(vec) }) +} + +pub(crate) fn parse_string_inner( + deserializer: &mut Deserializer, +) -> Result, Error> { + // Used to store strings. + let mut vec = Vec::new(); + + // Sets the starting position of the string. + deserializer.reader.start_caching(); + + loop { + match deserializer.reader.peek().map_err(Error::new_reader)? { + Some(ch) => { + // Improves character recognition speed (reduce the number of comparisons) by looking up tables. + // If it is an ordinary character, skips it. + if !ESCAPE[ch as usize] { + deserializer.reader.discard(); + continue; + } + match ch { + // When '"' is encountered, the string is added to vec. + QUOTATION_MARK => { + vec.extend_from_slice(deserializer.reader.cached_slice().unwrap()); + deserializer.reader.end_caching(); + deserializer.reader.discard(); + break; + } + // When '\\' is encountered, matches escape character. + REVERSE_SOLIDUS => { + vec.extend_from_slice(deserializer.reader.cached_slice().unwrap()); + deserializer.reader.discard(); + parse_escape_character(deserializer, &mut vec)?; + deserializer.reader.start_caching(); + } + + _ => { + // Other control characters are not output. + return unexpected_character!(deserializer); + } + } + } + None => return unexpected_eoj!(deserializer), + } + } + Ok(vec) +} + +// Parses escape characters. +fn parse_escape_character( + deserializer: &mut Deserializer, + vec: &mut Vec, +) -> Result<(), Error> { + vec.push( + match deserializer.reader.peek().map_err(Error::new_reader)? { + Some(QUOTATION_MARK) => QUOTATION_MARK, + Some(REVERSE_SOLIDUS) => REVERSE_SOLIDUS, + Some(SOLIDUS) => SOLIDUS, + Some(BS) => BS_UNICODE as u8, + Some(FF) => FF_UNICODE as u8, + Some(LF) => LF_UNICODE as u8, + Some(CR) => CR_UNICODE as u8, + Some(HT) => HT_UNICODE as u8, + Some(UNICODE) => { + deserializer.reader.discard(); + return parse_unicode(deserializer, vec); + } + Some(_) => return unexpected_character!(deserializer), + None => return unexpected_eoj!(deserializer), + }, + ); + deserializer.reader.discard(); + Ok(()) +} + +// Parses unicode +fn parse_unicode( + deserializer: &mut Deserializer, + vec: &mut Vec, +) -> Result<(), Error> { + // Reads a hexadecimal number. + #[inline] + fn get_next_digit(deserializer: &mut Deserializer) -> Result { + if let Some(ch) = deserializer.reader.peek().map_err(Error::new_reader)? { + let result = match ch { + ZERO..=NINE => ch as u16 - ZERO as u16, + A_LOWER..=F_LOWER => ch as u16 - A_LOWER as u16 + 10, + A_UPPER..=F_UPPER => ch as u16 - A_UPPER as u16 + 10, + _ => return unexpected_character!(deserializer), + }; + deserializer.reader.discard(); + return Ok(result); + } + unexpected_eoj!(deserializer) + } + + // Reads four hexadecimal digits consecutively. + #[inline] + fn get_next_four_digits( + deserializer: &mut Deserializer, + ) -> Result { + Ok(get_next_digit(deserializer)? << 12 + | get_next_digit(deserializer)? << 8 + | get_next_digit(deserializer)? << 4 + | get_next_digit(deserializer)?) + } + + // Unicode character logic: \uXXXX or \uXXXX\uXXXX + let unicode1 = get_next_four_digits(deserializer)?; + let unicode = match char::try_from(unicode1 as u32) { + Ok(code) => code, + Err(_) => { + match_str!(deserializer, UNICODE_START_STR); + + match core::char::decode_utf16( + [unicode1, get_next_four_digits(deserializer)?] + .iter() + .copied(), + ) + .next() + { + Some(Ok(code)) => code, + _ => return Err(Error::Utf8Transform), + } + } + }; + vec.extend_from_slice(unicode.encode_utf8(&mut [0; 4]).as_bytes()); + Ok(()) +} + +// Matches number. +pub(crate) fn parse_number( + deserializer: &mut Deserializer, +) -> Result { + // Sets the starting position of the string. + deserializer.reader.start_caching(); + + // `neg\dot\exp` determines which of `u64\i64\f64` will be used to represent the final number. + let mut neg = false; + let mut dot = false; + let mut exp = false; + + // Matches '-', JSON syntax does not match '+' + if let Some(MINUS) = deserializer.reader.peek().map_err(Error::new_reader)? { + deserializer.reader.discard(); + neg = true; + } + // `next_ch` temporarily saves unmatched characters after peek. + // Used to reduce the number of repeated peeks. + let mut next_ch = match deserializer.reader.peek().map_err(Error::new_reader)? { + // The integer part cannot have a leading 0, so if it encounters a 0 here, + // it enters the value 0 state directly. + Some(ZERO) => { + deserializer.reader.discard(); + // The reason to peek here is to compare with + // Some(ONE... =NINE) branches keep the same return value. + deserializer.reader.peek().map_err(Error::new_reader)? + } + Some(ONE..=NINE) => { + // Matches one digit character first. Ensure that there is at least one digit character. + deserializer.reader.discard(); + // Matches as many numeric characters as possible. + eat_digits_until_not!(deserializer) + } + Some(_) => return unexpected_character!(deserializer), + None => return unexpected_eoj!(deserializer), + }; + + // If there is a decimal point, matches fractional part. + if let Some(DECIMAL_POINT) = next_ch { + deserializer.reader.discard(); + dot = true; + + // Matches a numeric character. + match deserializer.reader.peek().map_err(Error::new_reader)? { + Some(ZERO..=NINE) => deserializer.reader.discard(), + Some(_) => return unexpected_character!(deserializer), + None => return unexpected_eoj!(deserializer), + }; + //Saves the extra characters for the next match. + next_ch = eat_digits_until_not!(deserializer) + } + + // If e is present, matches exponential part. + if let Some(E_LOWER | E_UPPER) = next_ch { + deserializer.reader.discard(); + exp = true; + // Try to match the sign of the exponential part, which can be without the sign. + match deserializer.reader.peek().map_err(Error::new_reader)? { + Some(PLUS | MINUS) => deserializer.reader.discard(), + Some(_) => {} + None => return unexpected_eoj!(deserializer), + } + // Matches a numeric character. + match deserializer.reader.peek().map_err(Error::new_reader)? { + Some(ZERO..=NINE) => deserializer.reader.discard(), + Some(_) => return unexpected_character!(deserializer), + None => return unexpected_eoj!(deserializer), + }; + // Matches the remaining numeric characters. + eat_digits_until_not!(deserializer); + } + + // The contents of u8 have been checked, so the unchecked method can be used here. + let str = + unsafe { core::str::from_utf8_unchecked(deserializer.reader.cached_slice().unwrap()) }; + let number = match (neg, dot, exp) { + (false, false, false) => { + Number::Unsigned(str.parse::().map_err(|_| Error::ParseNumber)?) + } + (true, false, false) => Number::Signed(str.parse::().map_err(|_| Error::ParseNumber)?), + (_, _, _) => Number::Float(str.parse::().map_err(|_| Error::ParseNumber)?), + }; + + deserializer.reader.end_caching(); + Ok(number) +} + +fn parse_array(deserializer: &mut Deserializer) -> Result { + enum InnerState { + Start, + AfterComma, + NoComma, + } + + deserializer.recursion_depth += 1; + check_recursion(deserializer)?; + + // Creates an Array to store value. + let mut array = Array::new(); + // The initial status is Start. + let mut state = InnerState::Start; + + loop { + match (state, eat_whitespace_until_not!(deserializer)) { + // In the initial state, if "]" is encountered, meaning the array is empty. + (InnerState::Start, Some(RIGHT_SQUARE_BRACKET)) => break, + // If in the initial state or "," has appeared, + // matches key-value pairs when any character is encountered. + (InnerState::Start | InnerState::AfterComma, _) => { + array.push(parse_value(deserializer)?); + + // Here sets the state to NoComma. + state = InnerState::NoComma; + } + // In NoComma state, the array ends when "]" is encountered. + (InnerState::NoComma, Some(RIGHT_SQUARE_BRACKET)) => break, + // In the NoComma state, when "," is encountered, converts to the HaveComma state. + (InnerState::NoComma, Some(COMMA)) => { + deserializer.reader.discard(); + state = InnerState::AfterComma; + } + // In the NoComma state, it is illegal to encounter any other character. + (InnerState::NoComma, Some(_)) => return unexpected_character!(deserializer), + // In all cases, None is illegal. + (_, None) => return unexpected_eoj!(deserializer), + } + } + deserializer.reader.discard(); + deserializer.recursion_depth -= 1; + Ok(JsonValue::Array(array)) +} + +pub(crate) fn read_error_char( + deserializer: &mut Deserializer, +) -> Result, Error> { + const CONT_MASK: u8 = 0b0011_1111; + + #[inline] + fn utf8_first_byte(byte: u8, width: u32) -> u32 { + (byte & (0x7F >> width)) as u32 + } + + #[inline] + fn utf8_acc_cont_byte(ch: u32, byte: u8) -> u32 { + (ch << 6) | (byte & CONT_MASK) as u32 + } + + let x = match deserializer.reader.next().map_err(Error::new_reader)? { + Some(x) => x, + None => return Ok(None), + }; + + let ch = if x < 128 { + x as u32 + } else { + let init = utf8_first_byte(x, 2); + + let y = match deserializer.reader.next().map_err(Error::new_reader)? { + Some(y) => y, + None => return Ok(None), + }; + + let mut ch = utf8_acc_cont_byte(init, y); + + if x >= 0xE0 { + let z = match deserializer.reader.next().map_err(Error::new_reader)? { + Some(z) => z, + None => return Ok(None), + }; + + let y_z = utf8_acc_cont_byte((y & CONT_MASK) as u32, z); + ch = init << 12 | y_z; + + if x >= 0xF0 { + let w = match deserializer.reader.next().map_err(Error::new_reader)? { + Some(w) => w, + None => return Ok(None), + }; + ch = (init & 7) << 18 | utf8_acc_cont_byte(y_z, w); + } + } + ch + }; + unsafe { Ok(Some(char::from_u32_unchecked(ch))) } +} + +#[cfg(test)] +mod ut_states { + use crate::reader::BytesReader; + use crate::states::*; + use std::io::{ErrorKind, Read}; + + struct ErrorIo; + + impl Read for ErrorIo { + fn read(&mut self, _buf: &mut [u8]) -> std::io::Result { + Err(ErrorKind::AddrInUse.into()) + } + } + + /// UT test for macro `eat_whitespace_until_not`. + /// + /// # Title + /// ut_macro_eat_whitespace_until_not + /// + /// # Brief + /// 1. Constructs various inputs. + /// 2. Uses macro `eat_whitespace_until_not`. + /// 3. Checks if the results are correct. + #[test] + fn ut_macro_eat_whitespace_until_not() { + fn test_func( + deserializer: &mut Deserializer, + ) -> Result, Error> { + Ok(eat_whitespace_until_not!(deserializer)) + } + + let mut deserializer = Deserializer::new_from_slice(b" n"); + assert_eq!(test_func(&mut deserializer).unwrap(), Some(b'n')); + + let mut deserializer = Deserializer::new_from_slice(b" "); + assert_eq!(test_func(&mut deserializer).unwrap(), None); + + let mut deserializer = Deserializer::new_from_io(ErrorIo); + assert!(test_func(&mut deserializer).is_err()); + } + + /// UT test for macro `eat_digits_until_not`. + /// + /// # Title + /// ut_macro_eat_digits_until_not + /// + /// # Brief + /// 1. Constructs various inputs. + /// 2. Uses macro `eat_digits_until_not`. + /// 3. Checks if the results are correct. + #[test] + fn ut_macro_eat_digits_until_not() { + fn test_func( + deserializer: &mut Deserializer, + ) -> Result, Error> { + Ok(eat_digits_until_not!(deserializer)) + } + + let mut deserializer = Deserializer::new_from_slice(b"1234n"); + assert_eq!(test_func(&mut deserializer).unwrap(), Some(b'n')); + + let mut deserializer = Deserializer::new_from_slice(b"1234"); + assert_eq!(test_func(&mut deserializer).unwrap(), None); + + let mut deserializer = Deserializer::new_from_io(ErrorIo); + assert!(test_func(&mut deserializer).is_err()); + } + + /// UT test for macro `match_str`. + /// + /// # Title + /// ut_macro_match_str + /// + /// # Brief + /// 1. Constructs various inputs. + /// 2. Uses macro `match_str`. + /// 3. Checks if the results are correct. + #[test] + fn ut_macro_match_str() { + #[allow(clippy::unit_arg)] + fn test_func( + deserializer: &mut Deserializer, + target: &[u8], + ) -> Result<(), Error> { + Ok(match_str!(deserializer, target)) + } + + let mut deserializer = Deserializer::new_from_slice(b"1234"); + assert!(test_func(&mut deserializer, b"1234").is_ok()); + + let mut deserializer = Deserializer::new_from_io(ErrorIo); + assert!(test_func(&mut deserializer, b"1234").is_err()); + } + + /// UT test for `start_parsing`. + /// + /// # Title + /// ut_start_parsing + /// + /// # Brief + /// 1. Constructs various inputs. + /// 2. Calls `start_parsing`. + /// 3. Checks if the results are correct. + #[test] + fn ut_start_parsing() { + let mut deserializer = Deserializer::new_from_slice(b"null"); + assert_eq!(start_parsing(&mut deserializer).unwrap(), JsonValue::Null); + + let mut deserializer = Deserializer::new_from_slice(b"null invalid"); + assert!(start_parsing(&mut deserializer).is_err()); + } + + /// UT test for `read_error_char`. + /// + /// # Title + /// ut_read_error_char + /// + /// # Brief + /// 1. Constructs various inputs. + /// 2. Calls `read_error_char`. + /// 3. Checks if the results are correct. + #[test] + fn ut_read_error_char() { + let mut deserializer = Deserializer::new_from_slice("𤭢".as_bytes()); + assert_eq!(read_error_char(&mut deserializer).unwrap(), Some('𤭢')); + + let mut deserializer = Deserializer::new_from_slice(&[]); + assert_eq!(read_error_char(&mut deserializer).unwrap(), None); + + let mut deserializer = Deserializer::new_from_slice(&[0xf0]); + assert_eq!(read_error_char(&mut deserializer).unwrap(), None); + + let mut deserializer = Deserializer::new_from_slice(&[0xf0, 0xa4]); + assert_eq!(read_error_char(&mut deserializer).unwrap(), None); + + let mut deserializer = Deserializer::new_from_slice(&[0xf0, 0xa4, 0xad]); + assert_eq!(read_error_char(&mut deserializer).unwrap(), None); + } + + /// UT test for `parse_value`. + /// + /// # Title + /// ut_parse_value + /// + /// # Brief + /// 1. Creates an instance of json. + /// 2. Calls the parsing function of State. + /// 3. Checks if the results are correct. + #[test] + fn ut_parse_value() { + let mut deserializer = Deserializer::new_from_slice(b"null"); + assert_eq!(parse_value(&mut deserializer).unwrap(), JsonValue::Null); + + let mut deserializer = Deserializer::new_from_slice(b"true"); + assert_eq!( + parse_value(&mut deserializer).unwrap(), + JsonValue::Boolean(true) + ); + + let mut deserializer = Deserializer::new_from_slice(b"false"); + assert_eq!( + parse_value(&mut deserializer).unwrap(), + JsonValue::Boolean(false) + ); + + let mut deserializer = Deserializer::new_from_slice(b"123"); + assert!(parse_value(&mut deserializer).is_ok()); + + let mut deserializer = Deserializer::new_from_slice(b"\"abc\""); + assert!(parse_value(&mut deserializer).is_ok()); + + let mut deserializer = Deserializer::new_from_slice(b"[1, 2, 3]"); + assert!(parse_value(&mut deserializer).is_ok()); + + let mut deserializer = Deserializer::new_from_slice(b"{\"key\":\"value\"}"); + assert!(parse_value(&mut deserializer).is_ok()); + + let mut deserializer = Deserializer::new_from_slice(b"\"abc\""); + assert!(parse_value(&mut deserializer).is_ok()); + } + + /// UT test for `parse_string`. + /// + /// # Title + /// ut_parse_string + /// + /// # Brief + /// 1. Creates an instance of Reader. + /// 2. Calls the parsing function of State. + /// 3. Checks if the results are correct. + #[test] + fn ut_parse_string() { + // 1.Enter a valid key (or String) and return a string. + // 2.Enter an invalid key (or string) and return an Error message. + + #[cfg(feature = "c_adapter")] + use std::ffi::CString; + + // Ensure that the previous '"' has been read before entering parse_string. + // Empty string + let str = "\""; + let mut deserializer = Deserializer::new_from_slice(str.as_bytes()); + #[cfg(not(feature = "c_adapter"))] + assert_eq!(parse_string(&mut deserializer).unwrap(), String::from("")); + #[cfg(feature = "c_adapter")] + assert_eq!( + parse_string(&mut deserializer).unwrap(), + CString::new("").unwrap() + ); + + // General character + let str = "abcdefghijklmnopqrstuvwxyz1234567890-=~!@#$%^&*()_+[]{}|<>?:;'\""; + let mut deserializer = Deserializer::new_from_slice(str.as_bytes()); + #[cfg(not(feature = "c_adapter"))] + assert_eq!( + parse_string(&mut deserializer).unwrap(), + String::from("abcdefghijklmnopqrstuvwxyz1234567890-=~!@#$%^&*()_+[]{}|<>?:;'"), + ); + #[cfg(feature = "c_adapter")] + assert_eq!( + parse_string(&mut deserializer).unwrap(), + CString::new("abcdefghijklmnopqrstuvwxyz1234567890-=~!@#$%^&*()_+[]{}|<>?:;'").unwrap(), + ); + + // Escape character + let str = r#"\/\\\"\uCAFE\uBABE\uAB98\uFCDE\ubcda\uef4A\b\f\n\r\t""#; + let mut deserializer = Deserializer::new_from_slice(str.as_bytes()); + #[cfg(not(feature = "c_adapter"))] + assert_eq!( + parse_string(&mut deserializer).unwrap(), + String::from( + "/\\\"\u{CAFE}\u{BABE}\u{AB98}\u{FCDE}\u{bcda}\u{ef4A}\u{0008}\u{000c}\n\r\t" + ), + ); + #[cfg(feature = "c_adapter")] + assert_eq!( + parse_string(&mut deserializer).unwrap(), + CString::new( + "/\\\"\u{CAFE}\u{BABE}\u{AB98}\u{FCDE}\u{bcda}\u{ef4A}\u{0008}\u{000c}\n\r\t" + ) + .unwrap(), + ); + + let str = r#"\uD852\uDF62""#; + let mut deserializer = Deserializer::new_from_slice(str.as_bytes()); + #[cfg(not(feature = "c_adapter"))] + assert_eq!(parse_string(&mut deserializer).unwrap(), String::from("𤭢"),); + #[cfg(feature = "c_adapter")] + assert_eq!( + parse_string(&mut deserializer).unwrap(), + CString::new("𤭢").unwrap(), + ); + + // Error scenes + // 1.There are no trailing quotes to end a match (or encounter a terminator). + let str = "abc"; + let mut deserializer = Deserializer::new_from_slice(str.as_bytes()); + assert!(parse_string(&mut deserializer).is_err()); + + // 2.Illegal escape character. + let str = r#"\g""#; + let mut deserializer = Deserializer::new_from_slice(str.as_bytes()); + assert!(parse_string(&mut deserializer).is_err()); + + // 3.A backslash is followed by a terminator. + let str = r#"\"#; + let mut deserializer = Deserializer::new_from_slice(str.as_bytes()); + assert!(parse_string(&mut deserializer).is_err()); + + // 4.Illegal unicode characters. + let str = r#"\uBEEF"#; + let mut deserializer = Deserializer::new_from_slice(str.as_bytes()); + assert!(parse_string(&mut deserializer).is_err()); + + let str = r#"\uZ000"#; + let mut deserializer = Deserializer::new_from_slice(str.as_bytes()); + assert!(parse_string(&mut deserializer).is_err()); + + let str = r#"\u"#; + let mut deserializer = Deserializer::new_from_slice(str.as_bytes()); + assert!(parse_string(&mut deserializer).is_err()); + + let str = r#"\uD852\uDB00""#; + let mut deserializer = Deserializer::new_from_slice(str.as_bytes()); + assert!(parse_string(&mut deserializer).is_err()); + + // 5.Control character. + let str = "\u{0}"; + let mut deserializer = Deserializer::new_from_slice(str.as_bytes()); + assert!(parse_string(&mut deserializer).is_err()); + } + + /// UT test for `parse_number`. + /// + /// # Title + /// ut_parse_number + /// + /// # Brief + /// 1. Creates an instance of Reader. + /// 2. Calls the parsing function of State. + /// 3. Checks if the results are correct. + #[test] + fn ut_parse_number() { + // 1.Enters a value (legal) and return a numeric value. + // 2.Enters a value (illegal) and return the corresponding Error. + // 3.Enters a value (text terminated prematurely, illegal) and return the corresponding Error. + + let str = r#"0"#; + let mut deserializer = Deserializer::new_from_slice(str.as_bytes()); + assert_eq!(parse_number(&mut deserializer).unwrap(), 0.into()); + + let str = r#"-0"#; + let mut deserializer = Deserializer::new_from_slice(str.as_bytes()); + assert_eq!(parse_number(&mut deserializer).unwrap(), 0.into()); + + let str = r#"0.123e+4"#; + let mut deserializer = Deserializer::new_from_slice(str.as_bytes()); + assert_eq!(parse_number(&mut deserializer).unwrap(), 1230.into()); + + // Error scenes. + // 1.No number exists. + let str = r#""#; + let mut deserializer = Deserializer::new_from_slice(str.as_bytes()); + assert!(parse_number(&mut deserializer).is_err()); + + // 2.Non-numeric characters exist. + let str = r#"a123"#; + let mut deserializer = Deserializer::new_from_slice(str.as_bytes()); + assert!(parse_number(&mut deserializer).is_err()); + + // 3.There is no integer part. + let str = r#".123"#; + let mut deserializer = Deserializer::new_from_slice(str.as_bytes()); + assert!(parse_number(&mut deserializer).is_err()); + + // 4.Positive numbers appear with a plus sign. + let str = r#"+1234"#; + let mut deserializer = Deserializer::new_from_slice(str.as_bytes()); + assert!(parse_number(&mut deserializer).is_err()); + + // 5.Integer part in front of a number of 0. + let str = r#"00001234"#; + let mut deserializer = Deserializer::new_from_slice(str.as_bytes()); + // In this case, only 0 will be read. + // The subsequent matching will cause an error when encounter a number. + assert_eq!(parse_number(&mut deserializer).unwrap(), 0.into()); + + // 6.The integer part contains other characters. + let str = r#"12a34"#; + let mut deserializer = Deserializer::new_from_slice(str.as_bytes()); + // In this case, only 12 will be read. + // The subsequent matching will cause an error when encounter 'a'. + assert_eq!(parse_number(&mut deserializer).unwrap(), 12.into()); + + // 7.The decimal part contains other characters. + let str = r#"12.a34"#; + let mut deserializer = Deserializer::new_from_slice(str.as_bytes()); + assert!(parse_number(&mut deserializer).is_err()); + + let str = r#"12."#; + let mut deserializer = Deserializer::new_from_slice(str.as_bytes()); + assert!(parse_number(&mut deserializer).is_err()); + + let str = r#"12.3a4"#; + let mut deserializer = Deserializer::new_from_slice(str.as_bytes()); + // In this case, only 12.3 will be read. + // The subsequent matching will cause an error when encounter 'a'. + assert_eq!(parse_number(&mut deserializer).unwrap(), (12.3).into()); + + // 8.The exponential part contains other characters. + let str = r#"12.34e+2a3"#; + let mut deserializer = Deserializer::new_from_slice(str.as_bytes()); + // In this case, only 12.34e+2 will be read. + // The subsequent matching will cause an error when encounter 'a'. + assert_eq!(parse_number(&mut deserializer).unwrap(), (1234).into()); + + let str = r#"12.34e"#; + let mut deserializer = Deserializer::new_from_slice(str.as_bytes()); + assert!(parse_number(&mut deserializer).is_err()); + + let str = r#"12.34ea"#; + let mut deserializer = Deserializer::new_from_slice(str.as_bytes()); + assert!(parse_number(&mut deserializer).is_err()); + + let str = r#"12.34e+"#; + let mut deserializer = Deserializer::new_from_slice(str.as_bytes()); + assert!(parse_number(&mut deserializer).is_err()); + } + + /// UT test for `ut_parse_array`. + /// + /// # Title + /// ut_parse_array + /// + /// # Brief + /// 1. Creates an instance of Reader. + /// 2. Calls the parsing function of State. + /// 3. Checks if the results are correct. + #[test] + fn ut_parse_array() { + // 1.Enters a value (legal) and return a numeric value. + // 2.Enters a value (illegal) and return the corresponding Error. + // 3.Enters a value (text terminated prematurely, illegal) and return the corresponding Error. + + // Before entering the parse_array function, needs to match '['. + let str = r#"]"#; + let mut deserializer = Deserializer::new_from_slice(str.as_bytes()); + assert_eq!(parse_array(&mut deserializer).unwrap(), Array::new().into()); + + let str = r#" ]"#; + let mut deserializer = Deserializer::new_from_slice(str.as_bytes()); + assert_eq!(parse_array(&mut deserializer).unwrap(), Array::new().into()); + + let str = r#"1, 2, 3]"#; + let mut deserializer = Deserializer::new_from_slice(str.as_bytes()); + let array = array!(1u8, 2u8, 3u8); + assert_eq!(parse_array(&mut deserializer).unwrap(), array.into()); + + let str = "\ + 1,\ + 2,\ + 3\ + ]"; + let mut deserializer = Deserializer::new_from_slice(str.as_bytes()); + let array = array!(1u8, 2u8, 3u8); + assert_eq!(parse_array(&mut deserializer).unwrap(), array.into()); + + // Error scenes. + // 1.Encounter terminator too early. + let str = ""; + let mut deserializer = Deserializer::new_from_slice(str.as_bytes()); + assert!(parse_array(&mut deserializer).is_err()); + + let str = "1 "; + let mut deserializer = Deserializer::new_from_slice(str.as_bytes()); + assert!(parse_array(&mut deserializer).is_err()); + + // 2.',' is not used between values. + let str = "1 2"; + let mut deserializer = Deserializer::new_from_slice(str.as_bytes()); + assert!(parse_array(&mut deserializer).is_err()); + + // 3.The extra ',' at the end. + let str = "1, 2,]"; + let mut deserializer = Deserializer::new_from_slice(str.as_bytes()); + assert!(parse_array(&mut deserializer).is_err()); + } + + /// UT test for `parse_object`. + /// + /// # Title + /// parse_object + /// + /// # Brief + /// 1. Creates an instance of Reader. + /// 2. Calls the parsing function of State. + /// 3. Checks if the results are correct. + #[test] + fn ut_parse_object() { + // 1.Enters a value (legal) and return a numeric value. + // 2.Enters a value (illegal) and return the corresponding Error. + // 3.Enters a value (text terminated prematurely, illegal) and return the corresponding Error. + + // Before entering parse_object, needs to match '{'. + let str = "}"; + let mut deserializer = Deserializer::new_from_slice(str.as_bytes()); + assert_eq!( + parse_object(&mut deserializer).unwrap(), + Object::new().into() + ); + + let str = "\"key1\": \"value\", \"key2\": \"value\"}"; + let mut deserializer = Deserializer::new_from_slice(str.as_bytes()); + let object = object!("key1" => "value"; "key2" => "value"); + assert_eq!(parse_object(&mut deserializer).unwrap(), object.into()); + + let str = "\ + \"key1\": \"value\",\ + \"key2\": \"value\"\ + }"; + let mut deserializer = Deserializer::new_from_slice(str.as_bytes()); + let object = object!("key1" => "value"; "key2" => "value"); + assert_eq!(parse_object(&mut deserializer).unwrap(), object.into()); + + // Error scenes. + // 1.Encounter terminator too early. + let str = ""; + let mut deserializer = Deserializer::new_from_slice(str.as_bytes()); + assert!(parse_object(&mut deserializer).is_err()); + + // 2.Encounter ',' too early. + let str = ","; + let mut deserializer = Deserializer::new_from_slice(str.as_bytes()); + assert!(parse_object(&mut deserializer).is_err()); + + // 3.The extra ',' at the end. + let str = "\"key1\": \"value\", \"key2\": \"value\",}"; + let mut deserializer = Deserializer::new_from_slice(str.as_bytes()); + assert!(parse_object(&mut deserializer).is_err()); + + // 4.There is no ':'. + let str = "\"key1\"t"; + let mut deserializer = Deserializer::new_from_slice(str.as_bytes()); + assert!(parse_object(&mut deserializer).is_err()); + + let str = "\"key1\""; + let mut deserializer = Deserializer::new_from_slice(str.as_bytes()); + assert!(parse_object(&mut deserializer).is_err()); + + // 5.Extra character. + let str = "\"key1\": 1 t"; + let mut deserializer = Deserializer::new_from_slice(str.as_bytes()); + assert!(parse_object(&mut deserializer).is_err()); + + let str = "\"key1\": 1, t"; + let mut deserializer = Deserializer::new_from_slice(str.as_bytes()); + assert!(parse_object(&mut deserializer).is_err()); + } + /// UT test for recursion limit. + /// + /// # Title + /// ut_recursion_limit + /// + /// # Brief + /// 1. Creates an instance exceeds recursion limit. + /// 2. Calls the parsing function of State. + /// 3. Checks if the results are correct. + #[test] + fn ut_recursion_limit() { + // Examples of array. + // This example has 128 layers of recursion(The upper recursion limit). + let text = r#" + [[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[ + [[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[ + [[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[ + [[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[ + ]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]] + ]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]] + ]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]] + ]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]] + "#; + let mut deserializer = Deserializer::new_from_slice(text.as_ref()); + assert!(start_parsing(&mut deserializer).is_ok()); + + // This example has 129 layers of recursion(The upper recursion limit is 128). + let text = r#" + [[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[ + [[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[ + [[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[ + [[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[ + ]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]] + ]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]] + ]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]] + ]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]] + "#; + let mut deserializer = Deserializer::new_from_slice(text.as_ref()); + assert!(start_parsing(&mut deserializer).is_err()); + + // Examples of object. + let mut str = String::from(r#"{"key":"value"}"#); + // 128 layers + for _i in 0..RECURSION_LIMIT - 1 { + str = str.replace(r#""value""#, r#"{"key":"value"}"#); + } + let text = str.as_bytes(); + let mut deserializer = Deserializer::new_from_slice(text); + assert!(start_parsing(&mut deserializer).is_ok()); + + let mut str = String::from(r#"{"key":"value"}"#); + // 129 layers + for _i in 0..RECURSION_LIMIT { + str = str.replace(r#""value""#, r#"{"key":"value"}"#); + } + let text = str.as_bytes(); + let mut deserializer = Deserializer::new_from_slice(text); + assert!(start_parsing(&mut deserializer).is_err()); + } +} diff --git a/src/value.rs b/src/value.rs new file mode 100644 index 0000000000000000000000000000000000000000..fe26448d2c7c6925e00f48e3e95ff65357179a79 --- /dev/null +++ b/src/value.rs @@ -0,0 +1,1769 @@ +/* + * Copyright (c) 2023 Huawei Device Co., Ltd. + * Licensed 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. + */ + +mod array; +mod index; +mod number; +mod object; + +pub use array::Array; +pub use index::Index; +pub use number::Number; +pub use object::Object; + +use crate::{start_parsing, CompactEncoder, Error, FormattedEncoder}; +use core::fmt::{Debug, Display, Formatter}; +use core::str::FromStr; +#[cfg(feature = "c_adapter")] +use std::ffi::CString; +use std::io::{Read, Write}; + +#[cfg(feature = "c_adapter")] +pub type JsonString = CString; +#[cfg(not(feature = "c_adapter"))] +pub type JsonString = String; + +use crate::deserializer::Deserializer; +#[cfg(not(feature = "c_adapter"))] +use std::fs::File; +#[cfg(not(feature = "c_adapter"))] +use std::path::Path; + +/// There are 6 types of values that appear in Json text: +/// +/// 1.Null: Null tupe +/// +/// 2.Boolean: Boolean type +/// +/// 3.Number: Numerical type +/// +/// 4.String: String type +/// +/// 5.Array: Array type +/// +/// 6.Object: Object type +/// +/// RFC 7159 3. Values say +/// “A Json value must be an object, an array, a number, a string, or a text string of false, null, or true.” +/// +/// # features +/// Uses"c_adatper" feature: +/// In order to adapt the C encapsulation layer interface, the structure changes the +/// underlying implementation of String, and uses CString to get the char* pointer easily. +// TODO: Enhance the encapsulation of JsonValue, makes users can't use enum directly. +#[derive(Clone)] +pub enum JsonValue { + /// Null type + Null, + + /// Boolean type + Boolean(bool), + + /// Numerical type + Number(Number), + + /// String type + String(JsonString), + + /// Array type + Array(Array), + + /// Object type + Object(Object), +} + +/// JsonValue print method 1, prints the content directly (without extra double quotes). +/// +/// # Examples +/// ``` +/// use ylong_json::JsonValue; +/// +/// let value: JsonValue = "hello".into(); +/// let string = format!("{value}"); +/// assert_eq!(string, "\"hello\""); +/// ``` +impl Display for JsonValue { + fn fmt(&self, f: &mut Formatter<'_>) -> core::fmt::Result { + match self { + Self::Null => write!(f, "null"), + Self::Boolean(b) => write!(f, "{b}"), + Self::Number(n) => Display::fmt(n, f), + // The Debug output is the same for CString and String. + Self::String(s) => write!(f, "{s:?}"), + Self::Array(a) => Display::fmt(a, f), + Self::Object(o) => Display::fmt(o, f), + } + } +} + +impl Debug for JsonValue { + fn fmt(&self, f: &mut Formatter<'_>) -> core::fmt::Result { + Display::fmt(self, f) + } +} + +impl JsonValue { + /// Creates an instance of JsonValue for Null type. + /// + /// # Examples + /// ``` + /// use ylong_json::JsonValue; + /// + /// let value = JsonValue::new_null(); + /// assert_eq!(value, JsonValue::Null); + /// ``` + pub fn new_null() -> Self { + Self::Null + } + + /// Creates an instance of JsonValue for Boolean type. + /// + /// # Examples + /// ``` + /// use ylong_json::JsonValue; + /// + /// let value = JsonValue::new_boolean(true); + /// assert_eq!(value, JsonValue::Boolean(true)); + /// ``` + pub fn new_boolean(boolean: bool) -> Self { + Self::Boolean(boolean) + } + + /// Creates an instance of JsonValue for Numerical type. + /// + /// # Examples + /// ``` + /// use ylong_json::JsonValue; + /// + /// let value = JsonValue::new_number(0.0.into()); + /// assert_eq!(value, JsonValue::Number(0.0.into())); + /// ``` + pub fn new_number(number: Number) -> Self { + Self::Number(number) + } + + /// Creates an instance of JsonValue for String type. + /// + /// # Examples + /// ```not run + /// use ylong_json::JsonValue; + /// + /// let value = JsonValue::new_string("Hello World"); + /// // When open "c_adapter" feature, the internal value is String. + /// assert_eq!(value, JsonValue::String(String::from("Hello World"))); + /// + /// // When opening "c_adapter" feature, the internal value is CString. + /// // assert_eq!(value, JsonValue::String(CString::new("Hello World"))); + /// ``` + pub fn new_string(str: &str) -> Self { + // The underlying implementation of String here is CString. + #[cfg(feature = "c_adapter")] + let result = Self::String(JsonString::new(str).unwrap()); + + #[cfg(not(feature = "c_adapter"))] + let result = Self::String(JsonString::from(str)); + + result + } + + /// Creates an instance of JsonValue for Array type. + /// + /// # Examples + /// ``` + /// use ylong_json::{JsonValue, Array}; + /// + /// let value = JsonValue::new_array(Array::new()); + /// assert_eq!(value, JsonValue::Array(Array::new())); + /// ``` + pub fn new_array(array: Array) -> Self { + Self::Array(array) + } + + /// Creates an instance of JsonValue for Object type. + /// + /// # Examples + /// ``` + /// use ylong_json::{JsonValue, Object}; + /// + /// let value = JsonValue::new_object(Object::new()); + /// assert_eq!(value, JsonValue::Object(Object::new())); + /// ``` + pub fn new_object(object: Object) -> Self { + Self::Object(object) + } + + /// Determines whether JsonValue is Null type. Returns true if yes, false if no. + /// + /// # Examples + /// ``` + /// use ylong_json::JsonValue; + /// + /// let null_value = JsonValue::new_null(); + /// assert_eq!(null_value.is_null(), true); + /// + /// let other_value = JsonValue::new_number(0.0.into()); + /// assert_eq!(other_value.is_null(), false); + /// ``` + pub fn is_null(&self) -> bool { + matches!(*self, Self::Null) + } + + /// Determines whether JsonValue is Boolean type. Returns true if yes, false if no. + /// + /// # Examples + /// ``` + /// use ylong_json::JsonValue; + /// + /// let boolean_value = JsonValue::new_boolean(true); + /// assert_eq!(boolean_value.is_boolean(), true); + /// + /// let other_value = JsonValue::new_null(); + /// assert_eq!(other_value.is_boolean(), false); + /// ``` + pub fn is_boolean(&self) -> bool { + matches!(*self, Self::Boolean(_)) + } + + /// Determines whether JsonValue is true. Returns true if yes, false if no. + /// + /// # Examples + /// ``` + /// use ylong_json::JsonValue; + /// + /// let true_value = JsonValue::new_boolean(true); + /// assert_eq!(true_value.is_true(), true); + /// + /// let other_value = JsonValue::new_boolean(false); + /// assert_eq!(other_value.is_true(), false); + /// ``` + pub fn is_true(&self) -> bool { + match *self { + Self::Boolean(x) => x, + _ => false, + } + } + + /// Determines whether JsonValue is false. Returns true if yes, false if no. + /// + /// # Examples + /// ``` + /// use ylong_json::JsonValue; + /// + /// let false_value = JsonValue::new_boolean(false); + /// assert_eq!(false_value.is_false(), true); + /// + /// let other_value = JsonValue::new_boolean(true); + /// assert_eq!(other_value.is_false(), false); + /// ``` + pub fn is_false(&self) -> bool { + match *self { + Self::Boolean(x) => !x, + _ => false, + } + } + + /// Determines whether JsonValue is Numerical type. Returns true if yes, false if no. + /// + /// # Examples + /// ``` + /// use ylong_json::JsonValue; + /// + /// let number_value = JsonValue::new_number(0.0.into()); + /// assert_eq!(number_value.is_number(), true); + /// + /// let other_value = JsonValue::new_null(); + /// assert_eq!(other_value.is_number(), false); + /// ``` + pub fn is_number(&self) -> bool { + matches!(*self, Self::Number(_)) + } + + /// Determines whether JsonValue is String type. Returns true if yes, false if no. + /// + /// # Examples + /// ``` + /// use ylong_json::JsonValue; + /// + /// let string_value = JsonValue::new_string("Hello World"); + /// assert_eq!(string_value.is_string(), true); + /// + /// let other_value = JsonValue::new_null(); + /// assert_eq!(other_value.is_string(), false); + /// ``` + pub fn is_string(&self) -> bool { + matches!(*self, Self::String(_)) + } + + /// Determines whether JsonValue is Array type. Returns true if yes, false if no. + /// + /// # Examples + /// ``` + /// use ylong_json::{JsonValue, Array}; + /// + /// let array_value = JsonValue::new_array(Array::new()); + /// assert_eq!(array_value.is_array(), true); + /// + /// let other_value = JsonValue::new_null(); + /// assert_eq!(other_value.is_array(), false); + /// ``` + pub fn is_array(&self) -> bool { + matches!(*self, Self::Array(_)) + } + + /// Determines whether JsonValue is Object type. Returns true if yes, false if no. + /// + /// # Examples + /// ``` + /// use ylong_json::{JsonValue, Object}; + /// + /// let object_value = JsonValue::new_object(Object::new()); + /// assert_eq!(object_value.is_object(), true); + /// + /// let other_value = JsonValue::new_null(); + /// assert_eq!(other_value.is_object(), false); + /// ``` + pub fn is_object(&self) -> bool { + matches!(*self, Self::Object(_)) + } + + /// Trys to convert JsonValue to a common reference of Boolean type. Conversion failure will return Error. + /// + /// # Examples + /// ``` + /// use ylong_json::{JsonValue, Error}; + /// + /// let boolean_value = JsonValue::new_boolean(true); + /// assert_eq!(boolean_value.try_as_boolean().unwrap(), &true); + /// + /// let other_value = JsonValue::new_null(); + /// assert!(other_value.try_as_boolean().is_err()); + /// ``` + pub fn try_as_boolean(&self) -> Result<&bool, Error> { + match self { + Self::Boolean(boolean) => Ok(boolean), + _ => Err(Error::TypeTransform), + } + } + + /// Trys to convert JsonValue to a common reference of Numerical type. Conversion failure will return Error. + /// + /// # Examples + /// ``` + /// use ylong_json::{JsonValue, Number, Error}; + /// + /// let number_value = JsonValue::new_number(0.0.into()); + /// assert_eq!(number_value.try_as_number().unwrap(), &Number::from(0.0)); + /// + /// let other_value = JsonValue::new_null(); + /// assert!(other_value.try_as_number().is_err()); + /// ``` + pub fn try_as_number(&self) -> Result<&Number, Error> { + match self { + Self::Number(number) => Ok(number), + _ => Err(Error::TypeTransform), + } + } + + /// Trys to convert JsonValue to a common reference of String type. Conversion failure will return Error. + /// + /// # Examples + /// ```no_run + /// #[cfg(feature = "c_adapter")] + /// use std::ffi::CString; + /// use ylong_json::{JsonValue, Error}; + /// + /// let string_value = JsonValue::new_string("Hello World"); + /// #[cfg(feature = "c_adapter")] + /// assert_eq!(string_value.try_as_string().unwrap(), &CString::new("Hello World").unwrap()); + /// #[cfg(not(feature = "c_adapter"))] + /// assert_eq!(string_value.try_as_string().unwrap(), &String::from("Hello World")); + /// // When opening "c_adapter" feature, the underlying implementation is CString. + /// //assert_eq!(string_value.try_as_string().unwrap(), &CString::new("Hello World").unwrap()); + /// + /// let other_value = JsonValue::new_null(); + /// assert!(other_value.try_as_string().is_err()); + /// ``` + pub fn try_as_string(&self) -> Result<&JsonString, Error> { + match self { + Self::String(string) => Ok(string), + _ => Err(Error::TypeTransform), + } + } + + /// Trys to convert JsonValue to a common reference of Array type. Conversion failure will return Error. + /// + /// # Examples + /// ``` + /// use ylong_json::{JsonValue, Array, Error}; + /// + /// let array_value = JsonValue::new_array(Array::new()); + /// assert_eq!(array_value.try_as_array().unwrap(), &Array::new()); + /// + /// let other_value = JsonValue::new_null(); + /// assert!(other_value.try_as_array().is_err()); + /// ``` + pub fn try_as_array(&self) -> Result<&Array, Error> { + match self { + Self::Array(array) => Ok(array), + _ => Err(Error::TypeTransform), + } + } + + /// Trys to convert JsonValue to a common reference of Object type. Conversion failure will return Error. + /// + /// # Examples + /// ``` + /// use ylong_json::{JsonValue, Object, Error}; + /// + /// let array_value = JsonValue::new_object(Object::new()); + /// assert_eq!(array_value.try_as_object().unwrap(), &Object::new()); + /// + /// let other_value = JsonValue::new_null(); + /// assert!(other_value.try_as_object().is_err()); + /// ``` + pub fn try_as_object(&self) -> Result<&Object, Error> { + match self { + Self::Object(object) => Ok(object), + _ => Err(Error::TypeTransform), + } + } + + /// Trys to convert JsonValue to a mutable reference of Boolean type. Conversion failure will return Error. + /// + /// # Examples + /// ``` + /// use ylong_json::{JsonValue, Error}; + /// + /// let mut boolean_value = JsonValue::new_boolean(true); + /// assert_eq!(boolean_value.try_as_mut_boolean().unwrap(), &mut true); + /// + /// let mut other_value = JsonValue::new_null(); + /// assert!(other_value.try_as_mut_boolean().is_err()); + /// ``` + pub fn try_as_mut_boolean(&mut self) -> Result<&mut bool, Error> { + match self { + Self::Boolean(boolean) => Ok(boolean), + _ => Err(Error::TypeTransform), + } + } + + /// Trys to convert JsonValue to a mutable reference of Numerical type. Conversion failure will return Error. + /// + /// # Examples + /// ``` + /// use ylong_json::{JsonValue, Number, Error}; + /// + /// let mut number_value = JsonValue::new_number(0.0.into()); + /// assert_eq!(number_value.try_as_mut_number().unwrap(), &mut Number::from(0.0)); + /// + /// let mut other_value = JsonValue::new_null(); + /// assert!(other_value.try_as_mut_number().is_err()); + /// ``` + pub fn try_as_mut_number(&mut self) -> Result<&mut Number, Error> { + match self { + Self::Number(number) => Ok(number), + _ => Err(Error::TypeTransform), + } + } + + /// Trys to convert JsonValue to a mutable reference of String type. Conversion failure will return Error. + /// + /// # Examples + /// ```no_run + /// #[cfg(feature = "c_adapter")] + /// use std::ffi::CString; + /// use ylong_json::{JsonValue, Error}; + /// + /// let mut string_value = JsonValue::new_string("Hello World"); + /// #[cfg(feature = "c_adapter")] + /// assert_eq!(string_value.try_as_mut_string().unwrap(), &mut CString::new("Hello World").unwrap()); + /// #[cfg(not(feature = "c_adapter"))] + /// assert_eq!(string_value.try_as_mut_string().unwrap(), &mut String::from("Hello World")); + /// // When opening "c_adapter" feature, the underlying implementation is CString. + /// //assert_eq!(string_value.try_as_mut_string().unwrap(), &mut CString::new("Hello World").unwrap()); + /// + /// let mut other_value = JsonValue::new_null(); + /// assert!(other_value.try_as_mut_string().is_err()); + /// ``` + pub fn try_as_mut_string(&mut self) -> Result<&mut JsonString, Error> { + match self { + Self::String(string) => Ok(string), + _ => Err(Error::TypeTransform), + } + } + + /// Trys to convert JsonValue to a mutable reference of Array type. Conversion failure will return Error. + /// + /// # Examples + /// ``` + /// use ylong_json::{JsonValue, Error, Array}; + /// + /// let mut array_value = JsonValue::new_array(Array::new()); + /// assert_eq!(array_value.try_as_mut_array().unwrap(), &mut Array::new()); + /// + /// let mut other_value = JsonValue::new_null(); + /// assert!(other_value.try_as_mut_array().is_err()); + /// ``` + pub fn try_as_mut_array(&mut self) -> Result<&mut Array, Error> { + match self { + Self::Array(array) => Ok(array), + _ => Err(Error::TypeTransform), + } + } + + /// Trys to convert JsonValue to a mutable reference of Object type. Conversion failure will return Error. + /// + /// # Examples + /// ``` + /// use ylong_json::{JsonValue, Error, Object}; + /// + /// let mut object_value = JsonValue::new_object(Object::new()); + /// assert_eq!(object_value.try_as_mut_object().unwrap(), &mut Object::new()); + /// + /// let mut other_value = JsonValue::new_null(); + /// assert!(other_value.try_as_mut_object().is_err()); + /// ``` + pub fn try_as_mut_object(&mut self) -> Result<&mut Object, Error> { + match self { + Self::Object(object) => Ok(object), + _ => Err(Error::TypeTransform), + } + } + + /// Trys to convert JsonValue to Boolean type. This method transfers ownership. + /// Conversion failure will return Error. + /// + /// # Examples + /// ``` + /// use ylong_json::{JsonValue, Error}; + /// + /// let boolean_value = JsonValue::new_boolean(true); + /// assert_eq!(boolean_value.try_into_boolean().unwrap(), true); + /// + /// let other_value = JsonValue::new_null(); + /// assert!(other_value.try_into_boolean().is_err()); + /// ``` + pub fn try_into_boolean(self) -> Result { + match self { + Self::Boolean(boolean) => Ok(boolean), + _ => Err(Error::TypeTransform), + } + } + + /// Trys to convert JsonValue to Numerical type. This method transfers ownership. + /// Conversion failure will return Error. + /// + /// The value will be output as f64. If you want to output other types, use 'as' to convert. + /// + /// # Examples + /// ``` + /// use ylong_json::{JsonValue, Number, Error}; + /// + /// let number_value = JsonValue::new_number(0.0.into()); + /// assert_eq!(number_value.try_into_number().unwrap(), Number::from(0.0)); + /// + /// let other_value = JsonValue::new_null(); + /// assert!(other_value.try_into_number().is_err()); + /// ``` + pub fn try_into_number(self) -> Result { + match self { + Self::Number(number) => Ok(number), + _ => Err(Error::TypeTransform), + } + } + + /// Trys to convert JsonValue to String type. This method transfers ownership. + /// Conversion failure will return Error. + /// + /// # Examples + /// ```no_run + /// #[cfg(feature = "c_adapter")] + /// use std::ffi::CString; + /// use ylong_json::{JsonValue, Error}; + /// + /// let string_value = JsonValue::new_string("Hello World"); + /// #[cfg(feature = "c_adapter")] + /// assert_eq!(string_value.try_into_string().unwrap(), CString::new("Hello World").unwrap()); + /// #[cfg(not(feature = "c_adapter"))] + /// assert_eq!(string_value.try_into_string().unwrap(), String::from("Hello World")); + /// // When opening "c_adapter" feature, the underlying implementation is CString. + /// //assert_eq!(string_value.try_into_string().unwrap(), CString::new("Hello World").unwrap()); + /// + /// let other_value = JsonValue::new_null(); + /// assert!(other_value.try_into_string().is_err()); + /// ``` + pub fn try_into_string(self) -> Result { + match self { + Self::String(string) => Ok(string), + _ => Err(Error::TypeTransform), + } + } + + /// Trys to convert JsonValue to Array type. This method transfers ownership. + /// Conversion failure will return Error. + /// + /// # Examples + /// ``` + /// use ylong_json::{JsonValue, Error, Array}; + /// + /// let array_value = JsonValue::new_array(Array::new()); + /// assert_eq!(array_value.try_into_array().unwrap(), Array::new()); + /// + /// let other_value = JsonValue::new_null(); + /// assert!(other_value.try_into_array().is_err()); + /// ``` + pub fn try_into_array(self) -> Result { + match self { + Self::Array(array) => Ok(array), + _ => Err(Error::TypeTransform), + } + } + + /// Trys to convert JsonValue to Object type. This method transfers ownership. + /// Conversion failure will return Error. + /// + /// # Examples + /// ``` + /// use ylong_json::{JsonValue, Error, Object}; + /// + /// let object_value = JsonValue::new_object(Object::new()); + /// assert_eq!(object_value.try_into_object().unwrap(), Object::new()); + /// + /// let other_value = JsonValue::new_null(); + /// assert!(other_value.try_into_object().is_err()); + /// ``` + pub fn try_into_object(self) -> Result { + match self { + Self::Object(object) => Ok(object), + _ => Err(Error::TypeTransform), + } + } + + /// Trys to remove a member form an Object or Array. If the member is found by Index, + /// gets value of the member from the Object or Array. + /// + /// # Examples + /// ``` + /// use ylong_json::{Object, JsonValue}; + /// + /// let mut object = Object::new(); + /// object.insert(String::from("key"), "value".into()); + /// + /// let mut value: JsonValue = object.into(); + /// assert_eq!(value["key"], "value".into()); + /// + /// value.remove("key"); + /// assert_eq!(value["key"], JsonValue::Null); + /// ``` + pub fn remove(&mut self, index: I) -> Option { + index.index_remove(self) + } + + /// Reads the contents from the file and Trys to deserialize to a JsonValue instance. + /// + /// # Examples + /// ```not run + /// use ylong_json::JsonValue; + /// + /// let value = JsonValue::from_file("./json.txt").unwrap(); + /// ``` + #[cfg(not(feature = "c_adapter"))] + pub fn from_file>(path: P) -> Result { + let mut file = File::open(path.as_ref())?; + Self::from_reader(&mut file) + } + + /// Gets the text from an object that implements the Read trait provided + /// by the standard library and Trys to deserialize it into a JsonValue instance. + /// + /// # Examples + /// ```not run + /// use ylong_json::JsonValue; + /// use std::fs::File; + /// + /// let mut file = File::open("./json.txt").unwrap(); + /// let value = JsonValue::from_reader(&mut file).unwrap(); + /// ``` + pub fn from_reader(input: R) -> Result { + let mut deserializer = Deserializer::new_from_io(input); + start_parsing(&mut deserializer) + } + + /// Reads the text from a type that can be converted to [u8] and Trys to deserialize it to a Json instance. + /// + /// # Examples + /// ``` + /// use ylong_json::JsonValue; + /// + /// let text = r#" + /// { + /// "key": "value" + /// } + /// "#; + /// let value = JsonValue::from_text(text.as_bytes()).unwrap(); + /// + /// assert_eq!(value["key"], "value".into()); + /// ``` + pub fn from_text>(text: T) -> Result { + let mut deserializer = Deserializer::new_from_slice(text.as_ref()); + start_parsing(&mut deserializer) + } + + /// Serializes the JsonValue instance to a formatted string with additional whitespace characters. + /// + /// # Examples + /// ``` + /// use ylong_json::JsonValue; + /// + /// let text = r#"{ + /// "key": "value" + /// } + /// "#; + /// let value = JsonValue::from_text(text.as_bytes()).unwrap(); + /// let string = value.to_formatted_string().unwrap(); + /// assert_eq!(string, text); + /// ``` + pub fn to_formatted_string(&self) -> Result { + let mut vec = Vec::new(); + self.formatted_encode(&mut vec)?; + Ok(unsafe { std::string::String::from_utf8_unchecked(vec) }) + } + + /// Serializes the JsonValue instance to a one-line string with no additional whitespace. + /// + /// # Examples + /// ``` + /// use ylong_json::JsonValue; + /// + /// let text = r#"{"key":"value"}"#; + /// let value = JsonValue::from_text(text.as_bytes()).unwrap(); + /// let string = value.to_compact_string().unwrap(); + /// assert_eq!(string, text); + /// ``` + pub fn to_compact_string(&self) -> Result { + let mut vec = Vec::new(); + self.compact_encode(&mut vec)?; + Ok(unsafe { std::string::String::from_utf8_unchecked(vec) }) + } + + /// Serializes the JsonValue instance to a formatted string with additional whitespace characters. + /// And outputs to the specified location as a stream of bytes. + /// + /// # Examples + /// ``` + /// use ylong_json::JsonValue; + /// + /// let text = r#"{ + /// "key": "value" + /// } + /// "#; + /// let value = JsonValue::from_text(text.as_bytes()).unwrap(); + /// let mut vec = Vec::new(); + /// value.formatted_encode(&mut vec).unwrap(); + /// assert_eq!(vec, text.as_bytes()); + /// ``` + pub fn formatted_encode(&self, output: &mut W) -> Result<(), Error> { + let mut encoder = FormattedEncoder::new(output); + encoder.encode(self) + } + + /// Serializes the JsonValue instance to a one-line string with no additional whitespace. + /// And outputs to the specified location as a stream of bytes. + /// + /// # Examples + /// ``` + /// use ylong_json::JsonValue; + /// + /// let text = r#"{"key":"value"}"#; + /// let value = JsonValue::from_text(text.as_bytes()).unwrap(); + /// let mut vec = Vec::new(); + /// value.compact_encode(&mut vec).unwrap(); + /// assert_eq!(vec, text.as_bytes()); + /// ``` + pub fn compact_encode(&self, output: &mut W) -> Result<(), Error> { + let mut encoder = CompactEncoder::new(output); + encoder.encode(self) + } +} + +impl FromStr for JsonValue { + type Err = Error; + + /// Generates an instance of JsonValue from &str. + /// + /// # Examples + /// ``` + /// use core::str::FromStr; + /// use ylong_json::JsonValue; + /// + /// let text = r#" + /// { + /// "key": "value" + /// } + /// "#; + /// let value = JsonValue::from_str(text).unwrap(); + /// + /// assert_eq!(value["key"], "value".into()); + /// ``` + fn from_str(s: &str) -> Result { + Self::from_text(s) + } +} + +impl PartialEq for JsonValue { + /// Determines whether two Jsonvalues are equal. + /// Only the same type can be compared; different types simply return false. + /// + /// If types are the same, false is returned only if the internal variables are exactly equal. + /// For example, when comparing arrays, two Arrays are considered equal + /// only if all their JsonValues have the same position and value. + /// When comparing objects, two Objects are considered equal + /// only if all of their key/value pairs match one another. + /// + /// # Examples + /// ``` + /// use ylong_json::{JsonValue, Array, Object}; + /// + /// assert_eq!(JsonValue::new_null(), JsonValue::new_null()); + /// assert_eq!(JsonValue::new_boolean(true), JsonValue::new_boolean(true)); + /// assert_eq!(JsonValue::new_number(0.0.into()), JsonValue::new_number(0.0.into())); + /// assert_eq!(JsonValue::new_string(""), JsonValue::new_string("")); + /// assert_eq!(JsonValue::new_array(Array::new()), JsonValue::new_array(Array::new())); + /// assert_eq!(JsonValue::new_object(Object::new()), JsonValue::new_object(Object::new())); + /// + /// assert_ne!(JsonValue::new_null(), JsonValue::new_number(0.0.into())); + /// assert_ne!(JsonValue::new_boolean(true), JsonValue::new_boolean(false)); + /// + /// let mut array1 = Array::new(); + /// array1.push(JsonValue::new_null()); + /// + /// let mut array2 = Array::new(); + /// array2.push(JsonValue::new_number(0.0.into())); + /// assert_ne!(JsonValue::new_array(array1), JsonValue::new_array(array2)); + /// + /// let mut object1 = Object::new(); + /// object1.insert(String::from("Key"), JsonValue::new_null()); + /// + /// let mut object2 = Object::new(); + /// object2.insert(String::from("Key"), JsonValue::new_number(0.0.into())); + /// assert_ne!(JsonValue::new_object(object1), JsonValue::new_object(object2)); + /// ``` + fn eq(&self, other: &Self) -> bool { + match (self, other) { + (JsonValue::Null, JsonValue::Null) => true, + (JsonValue::Boolean(a), JsonValue::Boolean(b)) => a == b, + (JsonValue::Number(a), JsonValue::Number(b)) => a == b, + (JsonValue::String(a), JsonValue::String(b)) => a == b, + (JsonValue::Array(a), JsonValue::Array(b)) => a == b, + (JsonValue::Object(a), JsonValue::Object(b)) => a == b, + _ => false, + } + } +} + +impl core::ops::Index for JsonValue { + type Output = JsonValue; + + fn index(&self, index: I) -> &Self::Output { + index.index_into(self) + } +} + +impl core::ops::IndexMut for JsonValue { + fn index_mut(&mut self, index: I) -> &mut Self::Output { + index.index_into_mut(self) + } +} + +impl From<&str> for JsonValue { + /// Converts from &str to JsonValue. + /// + /// # Examples + /// ``` + /// use ylong_json::JsonValue; + /// + /// let value: JsonValue = "Hello World".into(); + /// ``` + fn from(t: &str) -> Self { + #[cfg(feature = "c_adapter")] + let result = Self::String(JsonString::new(t).unwrap()); + + #[cfg(not(feature = "c_adapter"))] + let result = Self::String(String::from(t)); + result + } +} + +impl From for JsonValue { + /// Converts from String to JsonValue. + /// + /// # Examples + /// ```not run + /// use ylong_json::JsonValue; + /// + /// // Unused "c_adapter" feature. + /// let value: JsonValue = String::from("Hello World").into(); + /// // Uses "c_adapter" feature. + /// // let value: JsonValue = CString::new("Hello World").into(); + /// ``` + fn from(t: JsonString) -> Self { + Self::String(t) + } +} + +macro_rules! json_value_from_type { + ($type: tt, $func: expr) => { + impl From<$type> for JsonValue { + #[doc = concat!("从 ", stringify!($type), " 转换为 JsonValue。")] + /// + /// # Examples + /// ``` + /// use ylong_json::*; + /// + #[doc = concat!("let value: JsonValue = ", stringify!($type), "::default().into();")] + /// ``` + fn from(t: $type) -> Self { + $func(t.into()) + } + } + + impl From<&$type> for JsonValue { + #[doc = concat!("从 &", stringify!($type), " 转换为 JsonValue。")] + /// + /// # Examples + /// ``` + /// use ylong_json::*; + /// + #[doc = concat!("let value: JsonValue = ", stringify!($type), "::default().into();")] + /// ``` + fn from(t: &$type) -> Self { + $func(t.clone().into()) + } + } + + impl From<&mut $type> for JsonValue { + #[doc = concat!("从 &mut", stringify!($type), " 转换为 JsonValue。")] + /// + /// # Examples + /// ``` + /// use ylong_json::*; + /// + #[doc = concat!("let value: JsonValue = ", stringify!($type), "::default().into();")] + /// ``` + fn from(t: &mut $type) -> Self { + $func(t.clone().into()) + } + } + }; +} + +macro_rules! number_value_from_type { + ($($type: tt),* $(,)?) => { + $( + impl From<$type> for JsonValue { + #[doc = concat!("从 ", stringify!($type), " 转换为 JsonValue。")] + /// + /// # Examples + /// ``` + /// use ylong_json::JsonValue; + /// + /// // Due to the conversion to f64, there may be a loss of accuracy. + #[doc = concat!("let value: JsonValue = ", stringify!($type), "::MAX.into();")] + /// ``` + fn from(t: $type) -> Self { + Self::Number(Number::from(t)) + } + } + )* + } +} + +json_value_from_type!(bool, JsonValue::new_boolean); +json_value_from_type!(Array, JsonValue::new_array); +json_value_from_type!(Object, JsonValue::new_object); + +number_value_from_type!(i8, i16, i32, i64, isize, u8, u16, u32, u64, usize, f32, f64); + +#[cfg(test)] +mod ut_json_value { + use super::{array::Array, object::Object, JsonValue}; + use std::io::{ErrorKind, Read, Result}; + use std::str::FromStr; + + /// UT test for `JsonValue::fmt`. + /// + /// # Title + /// ut_json_value_fmt + /// + /// # Brief + /// 1. Creates some `JsonValue`s. + /// 2. Calls `Display::fmt` and `Debug::fmt`. + /// 3. Checks if the test results are correct. + #[test] + fn ut_json_value_fmt() { + let value = JsonValue::new_null(); + assert_eq!(format!("{value}"), "null"); + assert_eq!(format!("{value:?}"), "null"); + + let value = JsonValue::new_boolean(false); + assert_eq!(format!("{value}"), "false"); + assert_eq!(format!("{value:?}"), "false"); + + let value = JsonValue::new_number(12.34.into()); + assert_eq!(format!("{value}"), "12.34"); + assert_eq!(format!("{value:?}"), "12.34"); + + let value = JsonValue::new_string("Hello"); + assert_eq!(format!("{value}"), "\"Hello\""); + assert_eq!(format!("{value:?}"), "\"Hello\""); + + let value = JsonValue::new_array(array!(false, JsonValue::Null, 12.34)); + assert_eq!(format!("{value}"), "[false,null,12.34]"); + assert_eq!(format!("{value}"), "[false,null,12.34]"); + + let object = object!("null" => JsonValue::Null); + let value = JsonValue::new_object(object); + assert_eq!(format!("{value}"), "{\"null\":null}"); + assert_eq!(format!("{value}"), "{\"null\":null}"); + } + + /// UT test for `JsonValue::clone`. + /// + /// # Title + /// ut_json_value_fmt + /// + /// # Brief + /// 1. Creates some `JsonValue`s. + /// 2. Calls `JsonValue::clone`. + /// 3. Checks if the test results are correct. + #[test] + fn ut_json_value_clone() { + let value1 = JsonValue::new_null(); + assert_eq!(value1, value1.clone()); + } + + /// UT test for `JsonValue::is_null`. + /// + /// # Title + /// ut_json_value_is_null + /// + /// # Brief + /// 1. Creates some `JsonValue`s. + /// 2. Calls `JsonValue::is_null`. + /// 3. Checks if the test results are correct. + #[test] + fn ut_json_value_is_null() { + assert!(JsonValue::new_null().is_null()); + assert!(!JsonValue::new_boolean(true).is_null()); + assert!(!JsonValue::new_boolean(false).is_null()); + assert!(!JsonValue::new_number(12.34.into()).is_null()); + assert!(!JsonValue::new_string("hello").is_null()); + assert!(!JsonValue::new_array(Array::new()).is_null()); + assert!(!JsonValue::new_object(Object::new()).is_null()); + } + + /// UT test for `JsonValue::is_true`. + /// + /// # Title + /// ut_json_value_is_true + /// + /// # Brief + /// 1. Creates some `JsonValue`s. + /// 2. Calls `JsonValue::is_true`. + /// 3. Checks if the test results are correct. + #[test] + fn ut_json_value_is_true() { + assert!(!JsonValue::new_null().is_true()); + assert!(JsonValue::new_boolean(true).is_true()); + assert!(!JsonValue::new_boolean(false).is_true()); + assert!(!JsonValue::new_number(12.34.into()).is_true()); + assert!(!JsonValue::new_string("hello").is_true()); + assert!(!JsonValue::new_array(Array::new()).is_true()); + assert!(!JsonValue::new_object(Object::new()).is_true()); + } + + /// UT test for `JsonValue::is_false`. + /// + /// # Title + /// ut_json_value_is_false + /// + /// # Brief + /// 1. Creates some `JsonValue`s. + /// 2. Calls `JsonValue::is_false`. + /// 3. Checks if the test results are correct. + #[test] + fn ut_json_value_is_false() { + assert!(!JsonValue::new_null().is_false()); + assert!(!JsonValue::new_boolean(true).is_false()); + assert!(JsonValue::new_boolean(false).is_false()); + assert!(!JsonValue::new_number(12.34.into()).is_false()); + assert!(!JsonValue::new_string("hello").is_false()); + assert!(!JsonValue::new_array(Array::new()).is_false()); + assert!(!JsonValue::new_object(Object::new()).is_false()); + } + + /// UT test for `JsonValue::is_boolean`. + /// + /// # Title + /// ut_json_value_is_false + /// + /// # Brief + /// 1. Creates some `JsonValue`s. + /// 2. Calls `JsonValue::is_boolean`. + /// 3. Checks if the test results are correct. + #[test] + fn ut_json_value_is_boolean() { + assert!(!JsonValue::new_null().is_boolean()); + assert!(JsonValue::new_boolean(true).is_boolean()); + assert!(JsonValue::new_boolean(false).is_boolean()); + assert!(!JsonValue::new_number(12.34.into()).is_boolean()); + assert!(!JsonValue::new_string("hello").is_boolean()); + assert!(!JsonValue::new_array(Array::new()).is_boolean()); + assert!(!JsonValue::new_object(Object::new()).is_boolean()); + } + + /// UT test for `JsonValue::is_number`. + /// + /// # Title + /// ut_json_value_is_number + /// + /// # Brief + /// 1. Creates some `JsonValue`s. + /// 2. Calls `JsonValue::is_number`. + /// 3. Checks if the test results are correct. + #[test] + fn ut_json_value_is_number() { + assert!(!JsonValue::new_null().is_number()); + assert!(!JsonValue::new_boolean(true).is_number()); + assert!(!JsonValue::new_boolean(false).is_number()); + assert!(JsonValue::new_number(12.34.into()).is_number()); + assert!(!JsonValue::new_string("hello").is_number()); + assert!(!JsonValue::new_array(Array::new()).is_number()); + assert!(!JsonValue::new_object(Object::new()).is_number()); + } + + /// UT test for `JsonValue::is_string`. + /// + /// # Title + /// ut_json_value_is_string + /// + /// # Brief + /// 1. Creates some `JsonValue`s. + /// 2. Calls `JsonValue::is_string`. + /// 3. Checks if the test results are correct. + #[test] + fn ut_json_value_is_string() { + assert!(!JsonValue::new_null().is_string()); + assert!(!JsonValue::new_boolean(true).is_string()); + assert!(!JsonValue::new_boolean(false).is_string()); + assert!(!JsonValue::new_number(12.34.into()).is_string()); + assert!(JsonValue::new_string("hello").is_string()); + assert!(!JsonValue::new_array(Array::new()).is_string()); + assert!(!JsonValue::new_object(Object::new()).is_string()); + } + + /// UT test for `JsonValue::is_array`. + /// + /// # Title + /// ut_json_value_is_array + /// + /// # Brief + /// 1. Creates some `JsonValue`s. + /// 2. Calls `JsonValue::is_array`. + /// 3. Checks if the test results are correct. + #[test] + fn ut_json_value_is_array() { + assert!(!JsonValue::new_null().is_array()); + assert!(!JsonValue::new_boolean(true).is_array()); + assert!(!JsonValue::new_boolean(false).is_array()); + assert!(!JsonValue::new_number(12.34.into()).is_array()); + assert!(!JsonValue::new_string("hello").is_array()); + assert!(JsonValue::new_array(Array::new()).is_array()); + assert!(!JsonValue::new_object(Object::new()).is_array()); + } + + /// UT test for `JsonValue::is_object`. + /// + /// # Title + /// ut_json_value_is_object + /// + /// # Brief + /// 1. Creates some `JsonValue`s. + /// 2. Calls `JsonValue::is_object`. + /// 3. Checks if the test results are correct. + #[test] + fn ut_json_value_is_object() { + assert!(!JsonValue::new_null().is_object()); + assert!(!JsonValue::new_boolean(true).is_object()); + assert!(!JsonValue::new_boolean(false).is_object()); + assert!(!JsonValue::new_number(12.34.into()).is_object()); + assert!(!JsonValue::new_string("hello").is_object()); + assert!(!JsonValue::new_array(Array::new()).is_object()); + assert!(JsonValue::new_object(Object::new()).is_object()); + } + + /// UT test for `JsonValue::try_as_boolean`. + /// + /// # Title + /// ut_json_value_try_as_boolean + /// + /// # Brief + /// 1. Creates some `JsonValue`s. + /// 2. Calls `JsonValue::try_as_boolean`. + /// 3. Checks if the test results are correct. + #[test] + fn ut_json_value_try_as_boolean() { + assert!(JsonValue::new_null().try_as_boolean().is_err()); + assert!(JsonValue::new_boolean(true).try_as_boolean().is_ok()); + assert!(JsonValue::new_boolean(false).try_as_boolean().is_ok()); + assert!(JsonValue::new_number(12.34.into()) + .try_as_boolean() + .is_err()); + assert!(JsonValue::new_string("hello").try_as_boolean().is_err()); + assert!(JsonValue::new_array(Array::new()).try_as_boolean().is_err()); + assert!(JsonValue::new_object(Object::new()) + .try_as_boolean() + .is_err()); + } + + /// UT test for `JsonValue::try_as_number`. + /// + /// # Title + /// ut_json_value_try_as_number + /// + /// # Brief + /// 1. Creates some `JsonValue`s. + /// 2. Calls `JsonValue::try_as_number`. + /// 3. Checks if the test results are correct. + #[test] + fn ut_json_value_try_as_number() { + assert!(JsonValue::new_null().try_as_number().is_err()); + assert!(JsonValue::new_boolean(true).try_as_number().is_err()); + assert!(JsonValue::new_boolean(false).try_as_number().is_err()); + assert!(JsonValue::new_number(12.34.into()).try_as_number().is_ok()); + assert!(JsonValue::new_string("hello").try_as_number().is_err()); + assert!(JsonValue::new_array(Array::new()).try_as_number().is_err()); + assert!(JsonValue::new_object(Object::new()) + .try_as_number() + .is_err()); + } + + /// UT test for `JsonValue::try_as_string`. + /// + /// # Title + /// ut_json_value_try_as_string + /// + /// # Brief + /// 1. Creates some `JsonValue`s. + /// 2. Calls `JsonValue::try_as_string`. + /// 3. Checks if the test results are correct. + #[test] + fn ut_json_value_try_as_string() { + assert!(JsonValue::new_null().try_as_string().is_err()); + assert!(JsonValue::new_boolean(true).try_as_string().is_err()); + assert!(JsonValue::new_boolean(false).try_as_string().is_err()); + assert!(JsonValue::new_number(12.34.into()).try_as_string().is_err()); + assert!(JsonValue::new_string("hello").try_as_string().is_ok()); + assert!(JsonValue::new_array(Array::new()).try_as_string().is_err()); + assert!(JsonValue::new_object(Object::new()) + .try_as_string() + .is_err()); + } + + /// UT test for `JsonValue::try_as_array`. + /// + /// # Title + /// ut_json_value_try_as_array + /// + /// # Brief + /// 1. Creates some `JsonValue`s. + /// 2. Calls `JsonValue::try_as_array`. + /// 3. Checks if the test results are correct. + #[test] + fn ut_json_value_try_as_array() { + assert!(JsonValue::new_null().try_as_array().is_err()); + assert!(JsonValue::new_boolean(true).try_as_array().is_err()); + assert!(JsonValue::new_boolean(false).try_as_array().is_err()); + assert!(JsonValue::new_number(12.34.into()).try_as_array().is_err()); + assert!(JsonValue::new_string("hello").try_as_array().is_err()); + assert!(JsonValue::new_array(Array::new()).try_as_array().is_ok()); + assert!(JsonValue::new_object(Object::new()).try_as_array().is_err()); + } + + /// UT test for `JsonValue::try_as_object`. + /// + /// # Title + /// ut_json_value_try_as_object + /// + /// # Brief + /// 1. Creates some `JsonValue`s. + /// 2. Calls `JsonValue::try_as_object`. + /// 3. Checks if the test results are correct. + #[test] + fn ut_json_value_try_as_object() { + assert!(JsonValue::new_null().try_as_object().is_err()); + assert!(JsonValue::new_boolean(true).try_as_object().is_err()); + assert!(JsonValue::new_boolean(false).try_as_object().is_err()); + assert!(JsonValue::new_number(12.34.into()).try_as_object().is_err()); + assert!(JsonValue::new_string("hello").try_as_object().is_err()); + assert!(JsonValue::new_array(Array::new()).try_as_object().is_err()); + assert!(JsonValue::new_object(Object::new()).try_as_object().is_ok()); + } + + /// UT test for `JsonValue::try_as_mut_boolean`. + /// + /// # Title + /// ut_json_value_try_as_mut_boolean + /// + /// # Brief + /// 1. Creates some `JsonValue`s. + /// 2. Calls `JsonValue::try_as_mut_boolean`. + /// 3. Checks if the test results are correct. + #[test] + fn ut_json_value_try_as_mut_boolean() { + assert!(JsonValue::new_null().try_as_mut_boolean().is_err()); + assert!(JsonValue::new_boolean(true).try_as_mut_boolean().is_ok()); + assert!(JsonValue::new_boolean(false).try_as_mut_boolean().is_ok()); + assert!(JsonValue::new_number(12.34.into()) + .try_as_mut_boolean() + .is_err()); + assert!(JsonValue::new_string("hello").try_as_mut_boolean().is_err()); + assert!(JsonValue::new_array(Array::new()) + .try_as_mut_boolean() + .is_err()); + assert!(JsonValue::new_object(Object::new()) + .try_as_mut_boolean() + .is_err()); + } + + /// UT test for `JsonValue::try_as_mut_number`. + /// + /// # Title + /// ut_json_value_try_as_mut_number + /// + /// # Brief + /// 1. Creates some `JsonValue`s. + /// 2. Calls `JsonValue::try_as_mut_number`. + /// 3. Checks if the test results are correct. + #[test] + fn ut_json_value_try_as_mut_number() { + assert!(JsonValue::new_null().try_as_mut_number().is_err()); + assert!(JsonValue::new_boolean(true).try_as_mut_number().is_err()); + assert!(JsonValue::new_boolean(false).try_as_mut_number().is_err()); + assert!(JsonValue::new_number(12.34.into()) + .try_as_mut_number() + .is_ok()); + assert!(JsonValue::new_string("hello").try_as_mut_number().is_err()); + assert!(JsonValue::new_array(Array::new()) + .try_as_mut_number() + .is_err()); + assert!(JsonValue::new_object(Object::new()) + .try_as_mut_number() + .is_err()); + } + + /// UT test for `JsonValue::try_as_mut_string`. + /// + /// # Title + /// ut_json_value_try_as_mut_string + /// + /// # Brief + /// 1. Creates some `JsonValue`s. + /// 2. Calls `JsonValue::try_as_mut_string`. + /// 3. Checks if the test results are correct. + #[test] + fn ut_json_value_try_as_mut_string() { + assert!(JsonValue::new_null().try_as_mut_string().is_err()); + assert!(JsonValue::new_boolean(true).try_as_mut_string().is_err()); + assert!(JsonValue::new_boolean(false).try_as_mut_string().is_err()); + assert!(JsonValue::new_number(12.34.into()) + .try_as_mut_string() + .is_err()); + assert!(JsonValue::new_string("hello").try_as_mut_string().is_ok()); + assert!(JsonValue::new_array(Array::new()) + .try_as_mut_string() + .is_err()); + assert!(JsonValue::new_object(Object::new()) + .try_as_mut_string() + .is_err()); + } + + /// UT test for `JsonValue::try_as_mut_array`. + /// + /// # Title + /// ut_json_value_try_as_mut_array + /// + /// # Brief + /// 1. Creates some `JsonValue`s. + /// 2. Calls `JsonValue::try_as_mut_array`. + /// 3. Checks if the test results are correct. + #[test] + fn ut_json_value_try_as_mut_array() { + assert!(JsonValue::new_null().try_as_mut_array().is_err()); + assert!(JsonValue::new_boolean(true).try_as_mut_array().is_err()); + assert!(JsonValue::new_boolean(false).try_as_mut_array().is_err()); + assert!(JsonValue::new_number(12.34.into()) + .try_as_mut_array() + .is_err()); + assert!(JsonValue::new_string("hello").try_as_mut_array().is_err()); + assert!(JsonValue::new_array(Array::new()) + .try_as_mut_array() + .is_ok()); + assert!(JsonValue::new_object(Object::new()) + .try_as_mut_array() + .is_err()); + } + + /// UT test for `JsonValue::try_as_mut_object`. + /// + /// # Title + /// ut_json_value_try_as_mut_object + /// + /// # Brief + /// 1. Creates some `JsonValue`s. + /// 2. Calls `JsonValue::try_as_mut_object`. + /// 3. Checks if the test results are correct. + #[test] + fn ut_json_value_try_as_mut_object() { + assert!(JsonValue::new_null().try_as_mut_object().is_err()); + assert!(JsonValue::new_boolean(true).try_as_mut_object().is_err()); + assert!(JsonValue::new_boolean(false).try_as_mut_object().is_err()); + assert!(JsonValue::new_number(12.34.into()) + .try_as_mut_object() + .is_err()); + assert!(JsonValue::new_string("hello").try_as_mut_object().is_err()); + assert!(JsonValue::new_array(Array::new()) + .try_as_mut_object() + .is_err()); + assert!(JsonValue::new_object(Object::new()) + .try_as_mut_object() + .is_ok()); + } + + /// UT test for `JsonValue::try_into_boolean`. + /// + /// # Title + /// ut_json_value_try_into_boolean + /// + /// # Brief + /// 1. Creates some `JsonValue`s. + /// 2. Calls `JsonValue::try_into_boolean`. + /// 3. Checks if the test results are correct. + #[test] + fn ut_json_value_try_into_boolean() { + assert!(JsonValue::new_null().try_into_boolean().is_err()); + assert!(JsonValue::new_boolean(true).try_into_boolean().is_ok()); + assert!(JsonValue::new_boolean(false).try_into_boolean().is_ok()); + assert!(JsonValue::new_number(12.34.into()) + .try_into_boolean() + .is_err()); + assert!(JsonValue::new_string("hello").try_into_boolean().is_err()); + assert!(JsonValue::new_array(Array::new()) + .try_into_boolean() + .is_err()); + assert!(JsonValue::new_object(Object::new()) + .try_into_boolean() + .is_err()); + } + + /// UT test for `JsonValue::try_into_number`. + /// + /// # Title + /// ut_json_value_try_into_number + /// + /// # Brief + /// 1. Creates some `JsonValue`s. + /// 2. Calls `JsonValue::try_into_number`. + /// 3. Checks if the test results are correct. + #[test] + fn ut_json_value_try_into_number() { + assert!(JsonValue::new_null().try_into_number().is_err()); + assert!(JsonValue::new_boolean(true).try_into_number().is_err()); + assert!(JsonValue::new_boolean(false).try_into_number().is_err()); + assert!(JsonValue::new_number(12.34.into()) + .try_into_number() + .is_ok()); + assert!(JsonValue::new_string("hello").try_into_number().is_err()); + assert!(JsonValue::new_array(Array::new()) + .try_into_number() + .is_err()); + assert!(JsonValue::new_object(Object::new()) + .try_into_number() + .is_err()); + } + + /// UT test for `JsonValue::try_into_string`. + /// + /// # Title + /// ut_json_value_try_into_string + /// + /// # Brief + /// 1. Creates some `JsonValue`s. + /// 2. Calls `JsonValue::try_into_string`. + /// 3. Checks if the test results are correct. + #[test] + fn ut_json_value_try_into_string() { + assert!(JsonValue::new_null().try_into_string().is_err()); + assert!(JsonValue::new_boolean(true).try_into_string().is_err()); + assert!(JsonValue::new_boolean(false).try_into_string().is_err()); + assert!(JsonValue::new_number(12.34.into()) + .try_into_string() + .is_err()); + assert!(JsonValue::new_string("hello").try_into_string().is_ok()); + assert!(JsonValue::new_array(Array::new()) + .try_into_string() + .is_err()); + assert!(JsonValue::new_object(Object::new()) + .try_into_string() + .is_err()); + } + + /// UT test for `JsonValue::try_into_array`. + /// + /// # Title + /// ut_json_value_try_into_array + /// + /// # Brief + /// 1. Creates some `JsonValue`s. + /// 2. Calls `JsonValue::try_into_array`. + /// 3. Checks if the test results are correct. + #[test] + fn ut_json_value_try_into_array() { + assert!(JsonValue::new_null().try_into_array().is_err()); + assert!(JsonValue::new_boolean(true).try_into_array().is_err()); + assert!(JsonValue::new_boolean(false).try_into_array().is_err()); + assert!(JsonValue::new_number(12.34.into()) + .try_into_array() + .is_err()); + assert!(JsonValue::new_string("hello").try_into_array().is_err()); + assert!(JsonValue::new_array(Array::new()).try_into_array().is_ok()); + assert!(JsonValue::new_object(Object::new()) + .try_into_array() + .is_err()); + } + + /// UT test for `JsonValue::try_into_object`. + /// + /// # Title + /// ut_json_value_try_into_object + /// + /// # Brief + /// 1. Creates some `JsonValue`s. + /// 2. Calls `JsonValue::try_into_object`. + /// 3. Checks if the test results are correct. + #[test] + fn ut_json_value_try_into_object() { + assert!(JsonValue::new_null().try_into_object().is_err()); + assert!(JsonValue::new_boolean(true).try_into_object().is_err()); + assert!(JsonValue::new_boolean(false).try_into_object().is_err()); + assert!(JsonValue::new_number(12.34.into()) + .try_into_object() + .is_err()); + assert!(JsonValue::new_string("hello").try_into_object().is_err()); + assert!(JsonValue::new_array(Array::new()) + .try_into_object() + .is_err()); + assert!(JsonValue::new_object(Object::new()) + .try_into_object() + .is_ok()); + } + + /// UT test for `JsonValue::to_formatted_string`. + /// + /// # Title + /// ut_json_value_to_formatted_string + /// + /// # Brief + /// 1. Creates some `JsonValue`s. + /// 2. Calls `JsonValue::to_formatted_string`. + /// 3. Checks if the test results are correct. + #[test] + fn ut_json_value_to_formatted_string() { + assert_eq!( + JsonValue::new_null().to_formatted_string().unwrap(), + "null\n" + ); + } + + /// UT test for `JsonValue::to_compact_string`. + /// + /// # Title + /// ut_json_value_to_compact_string + /// + /// # Brief + /// 1. Creates some `JsonValue`s. + /// 2. Calls `JsonValue::to_compact_string`. + /// 3. Checks if the test results are correct. + #[test] + fn ut_json_value_to_compact_string() { + assert_eq!(JsonValue::new_null().to_compact_string().unwrap(), "null"); + } + + /// UT test for `JsonValue::from_str`. + /// + /// # Title + /// ut_json_value_from_str + /// + /// # Brief + /// 1. Calls `JsonValue::from_str` to create a `JsonValue`. + /// 2. Checks if the test results are correct. + #[test] + fn ut_json_value_from_str() { + assert_eq!(JsonValue::from_str("null").unwrap(), JsonValue::new_null()); + } + + /// UT test for `JsonValue::eq`. + /// + /// # Title + /// ut_json_value_eq + /// + /// # Brief + /// 1. Creates some `JsonValue`s. + /// 2. Calls `JsonValue::eq`. + /// 3. Checks if the test results are correct. + #[test] + fn ut_json_value_eq() { + assert_eq!(JsonValue::new_null(), JsonValue::new_null()); + assert_eq!(JsonValue::new_boolean(true), JsonValue::new_boolean(true)); + assert_eq!( + JsonValue::new_number(1.into()), + JsonValue::new_number(1.into()) + ); + assert_eq!( + JsonValue::new_string("string"), + JsonValue::new_string("string") + ); + assert_eq!( + JsonValue::new_array(Array::new()), + JsonValue::new_array(Array::new()) + ); + assert_eq!( + JsonValue::new_object(Object::new()), + JsonValue::new_object(Object::new()) + ); + assert_ne!(JsonValue::new_null(), JsonValue::new_boolean(true)); + } + + /// UT test for `JsonValue::from`. + /// + /// # Title + /// ut_json_value_from + /// + /// # Brief + /// 1. Calls `JsonValue::from` to create `JsonValue`s. + /// 2. Checks if the test results are correct. + #[test] + fn ut_json_value_from() { + assert_eq!(JsonValue::from(true), JsonValue::new_boolean(true)); + assert_eq!(JsonValue::from(false), JsonValue::new_boolean(false)); + assert_eq!( + JsonValue::from(Array::new()), + JsonValue::new_array(Array::new()) + ); + assert_eq!( + JsonValue::from(Object::new()), + JsonValue::new_object(Object::new()) + ); + + assert_eq!(JsonValue::from(&true), JsonValue::new_boolean(true)); + assert_eq!(JsonValue::from(&false), JsonValue::new_boolean(false)); + assert_eq!( + JsonValue::from(&Array::new()), + JsonValue::new_array(Array::new()) + ); + assert_eq!( + JsonValue::from(&Object::new()), + JsonValue::new_object(Object::new()) + ); + + assert_eq!(JsonValue::from(&mut true), JsonValue::new_boolean(true)); + assert_eq!(JsonValue::from(&mut false), JsonValue::new_boolean(false)); + assert_eq!( + JsonValue::from(&mut Array::new()), + JsonValue::new_array(Array::new()) + ); + assert_eq!( + JsonValue::from(&mut Object::new()), + JsonValue::new_object(Object::new()) + ); + + #[cfg(not(feature = "c_adapter"))] + assert_eq!(JsonValue::from(String::new()), JsonValue::new_string("")); + + #[cfg(feature = "c_adapter")] + { + use std::ffi::CString; + assert_eq!( + JsonValue::from(CString::new("").unwrap()), + JsonValue::new_string("") + ); + } + } + + /// UT test for `JsonValue::remove`. + /// + /// # Title + /// ut_json_value_remove + /// + /// # Brief + /// 1. Creates some `JsonValue`. + /// 2. Calls `JsonValue::remove` on them. + /// 3. Checks if the test results are correct. + #[test] + fn ut_json_value_remove() { + let mut object = JsonValue::new_object( + object!("key1" => "value1"; "key2" => "value2"; "key3" => "value3"), + ); + assert_eq!(object["key1"], JsonValue::new_string("value1")); + assert_eq!(object["key2"], JsonValue::new_string("value2")); + assert_eq!(object["key3"], JsonValue::new_string("value3")); + + object.remove("key2"); + assert_eq!(object["key1"], JsonValue::new_string("value1")); + assert_eq!(object["key2"], JsonValue::new_null()); + assert_eq!(object["key3"], JsonValue::new_string("value3")); + + let mut array = JsonValue::new_array(array!(false, JsonValue::new_null(), 12.34)); + assert_eq!(array[0], JsonValue::new_boolean(false)); + assert_eq!(array[1], JsonValue::new_null()); + assert_eq!(array[2], JsonValue::new_number(12.34.into())); + + array.remove(1); + assert_eq!(array[0], JsonValue::new_boolean(false)); + assert_eq!(array[1], JsonValue::new_number(12.34.into())); + assert_eq!(array[2], JsonValue::new_null()); + } + + /// UT test for `JsonValue::from_reader`. + /// + /// # Title + /// ut_json_value_from_reader + /// + /// # Brief + /// 1. Calls `JsonValue::from_reader` to create some `JsonValue`. + /// 2. Checks if the test results are correct. + #[test] + fn ut_json_value_from_reader() { + struct TestErrorIo; + + impl Read for TestErrorIo { + fn read(&mut self, _buf: &mut [u8]) -> Result { + Err(ErrorKind::AddrInUse.into()) + } + } + + assert!(JsonValue::from_reader(TestErrorIo).is_err()); + } +} diff --git a/src/value/array.rs b/src/value/array.rs new file mode 100644 index 0000000000000000000000000000000000000000..8d1b512450f9702ff420df14bb611cbe3d18ab18 --- /dev/null +++ b/src/value/array.rs @@ -0,0 +1,24 @@ +/* + * Copyright (c) 2023 Huawei Device Co., Ltd. + * Licensed 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. + */ + +#[cfg(feature = "list_array")] +mod linked_list; +#[cfg(feature = "list_array")] +pub use linked_list::Array; + +#[cfg(feature = "vec_array")] +mod vec; +#[cfg(feature = "vec_array")] +pub use vec::Array; diff --git a/src/value/array/linked_list.rs b/src/value/array/linked_list.rs new file mode 100644 index 0000000000000000000000000000000000000000..550eeb36dbe891cf932e48e88cd053069771ae0f --- /dev/null +++ b/src/value/array/linked_list.rs @@ -0,0 +1,534 @@ +/* + * Copyright (c) 2023 Huawei Device Co., Ltd. + * Licensed 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. + */ + +use crate::{Cursor, CursorMut, Iter, IterMut, JsonValue, LinkedList, Node}; +use core::fmt::{Debug, Display, Formatter}; + +/// Array type, implemented using LinkedList. +/// +/// # Situation +/// * When the average number of Array entries does not exceed 15 (estimated value). +/// +/// * When the average number of Array entries exceeds 15 (estimated value), but do not or rarely made query operation. +/// +/// # Attention +/// * Only open `list_array` feature can be used, and conflicts with other array-related features. +/// +/// # Examples +/// ``` +/// use ylong_json::Array; +/// +/// let array = Array::new(); +/// ``` +#[derive(Default, Clone, PartialEq)] +pub struct Array { + inner: LinkedList, +} + +impl Array { + /// Creates an empty Array instance. + /// + /// # Examples + /// ``` + /// use ylong_json::Array; + /// + /// let array = Array::new(); + /// ``` + pub fn new() -> Self { + Self { + inner: LinkedList::new(), + } + } + + /// Gets length of Array. + /// + /// # Examples + /// ``` + /// use ylong_json::{Array, JsonValue}; + /// + /// let mut array = Array::new(); + /// assert_eq!(array.len(), 0); + /// + /// array.push(JsonValue::Null); + /// assert_eq!(array.len(), 1); + /// ``` + pub fn len(&self) -> usize { + self.inner.len() + } + + /// Determines whether Array is empty. + /// + /// # Examples + /// ``` + /// use ylong_json::{Array, JsonValue}; + /// + /// let mut array = Array::new(); + /// assert_eq!(array.is_empty(), true); + /// + /// array.push(JsonValue::Null); + /// assert_eq!(array.is_empty(), false); + /// ``` + pub fn is_empty(&self) -> bool { + self.inner.is_empty() + } + + /// Insert a new JsonValue at the end of Array. + /// + /// # Examples + /// ``` + /// use ylong_json::{Array, JsonValue}; + /// + /// let mut array = Array::new(); + /// assert_eq!(array.len(), 0); + /// + /// array.push(JsonValue::Null); + /// assert_eq!(array.len(), 1); + /// ``` + pub fn push(&mut self, value: JsonValue) { + self.inner.push_back(value); + } + + /// Pops the element at the end of Array. + /// + /// # Examples + /// ``` + /// use ylong_json::{Array, JsonValue}; + /// + /// let mut array = Array::new(); + /// assert_eq!(array.pop(), None); + /// + /// array.push(JsonValue::Null); + /// assert_eq!(array.pop(), Some(JsonValue::Null)); + /// ``` + pub fn pop(&mut self) -> Option { + self.inner.pop_back() + } + + /// Gets a common iterator of Array. + /// + /// # Examples + /// ``` + /// use ylong_json::Array; + /// + /// let array = Array::new(); + /// let iter = array.iter(); + /// ``` + pub fn iter(&self) -> Iter<'_, JsonValue> { + self.inner.iter() + } + + /// Gets a mutable iterator of Array. + /// + /// # Examples + /// ``` + /// use ylong_json::Array; + /// + /// let mut array = Array::new(); + /// let iter_mut = array.iter_mut(); + /// ``` + pub fn iter_mut(&mut self) -> IterMut<'_, JsonValue> { + self.inner.iter_mut() + } + + /// Returns a common reference to the specified index ** member ** in Array. + /// + /// # Examples + /// ``` + /// use ylong_json::{Array, JsonValue}; + /// + /// let mut array = Array::new(); + /// array.push(JsonValue::Null); + /// assert_eq!(array.get(0), Some(&JsonValue::Null)); + /// assert_eq!(array.get(1), None); + /// ``` + pub fn get(&self, index: usize) -> Option<&JsonValue> { + self.get_cursor(index)?.current() + } + + /// Returns a mutable reference to the specified index ** member ** in Array. + /// + /// # Examples + /// ``` + /// use ylong_json::{Array, JsonValue}; + /// + /// let mut array = Array::new(); + /// array.push(JsonValue::Null); + /// assert_eq!(array.get_mut(0), Some(&mut JsonValue::Null)); + /// assert_eq!(array.get_mut(1), None); + /// ``` + pub fn get_mut(&mut self, index: usize) -> Option<&mut JsonValue> { + // Using 'get_cursor_mut' causes a problem referencing temporary variables. + self.get_node_mut(index).map(|n| n.get_element_mut()) + } + + /// Returns a common reference to the trailing ** member ** in Array. + /// + /// # Examples + /// ``` + /// use ylong_json::{Array, JsonValue}; + /// + /// let mut array = Array::new(); + /// assert_eq!(array.last(), None); + /// array.push(JsonValue::Null); + /// assert_eq!(array.last(), Some(&JsonValue::Null)); + /// ``` + pub fn last(&self) -> Option<&JsonValue> { + self.inner.back() + } + + /// Returns a mutable reference to the trailing ** member ** in Array. + /// + /// # Examples + /// ``` + /// use ylong_json::{Array, JsonValue}; + /// + /// let mut array = Array::new(); + /// assert_eq!(array.last_mut(), None); + /// array.push(JsonValue::Null); + /// assert_eq!(array.last_mut(), Some(&mut JsonValue::Null)); + /// ``` + pub fn last_mut(&mut self) -> Option<&mut JsonValue> { + self.inner.back_mut() + } + + /// Removes the node in Array with the specified index. + /// + /// # Examples + /// ``` + /// use ylong_json::{Array, JsonValue}; + /// + /// let mut array = Array::new(); + /// array.push(JsonValue::Null); + /// array.push(JsonValue::Boolean(true)); + /// array.push(JsonValue::Null); + /// + /// assert_eq!(array.len(), 3); + /// let second = array.remove(1); + /// assert_eq!(second, Some(JsonValue::Boolean(true))); + /// assert_eq!(array.len(), 2); + /// ``` + pub fn remove(&mut self, index: usize) -> Option { + self.get_cursor_mut(index)?.remove_current() + } + + /// Returns a common reference to the specified index ** node ** in Array. + /// + /// After getting a common reference to a node, the corresponding node cannot be released. + /// Otherwise undefined behavior will occur. + /// + /// # Examples + /// ``` + /// use ylong_json::{Array, JsonValue}; + /// + /// let mut array = Array::new(); + /// array.push(JsonValue::Null); + /// assert_eq!(array.get_node(0).is_some(), true); + /// assert_eq!(array.get_node(1).is_none(), true); + /// ``` + pub fn get_node(&self, index: usize) -> Option<&Node> { + self.get_cursor(index)?.current_node() + } + + /// Returns a mutable reference to the specified index ** node ** in Array. + /// + /// After getting a mutable reference to a node, the corresponding node cannot be released. + /// Otherwise undefined behavior will occur. + /// + /// # Examples + /// ``` + /// use ylong_json::{Array, JsonValue}; + /// + /// let mut array = Array::new(); + /// let value = JsonValue::Null; + /// array.push(value); + /// assert_eq!(array.get_node_mut(0).is_some(), true); + /// assert_eq!(array.get_node_mut(1).is_none(), true); + /// ``` + pub fn get_node_mut(&mut self, index: usize) -> Option<&mut Node> { + self.get_cursor_mut(index)?.current_node() + } + + /// Returns a common reference to the trailing ** node ** in Array. + /// + /// # Examples + /// ``` + /// use ylong_json::{Array, JsonValue}; + /// + /// let mut array = Array::new(); + /// assert_eq!(array.last_node().is_none(), true); + /// array.push(JsonValue::Null); + /// assert_eq!(array.last_node().is_some(), true); + /// ``` + pub fn last_node(&self) -> Option<&Node> { + self.inner.back_node() + } + + /// Returns a mutable reference to the trailing ** node ** in Array. + /// + /// # Examples + /// ``` + /// use ylong_json::{Array, JsonValue}; + /// + /// let mut array = Array::new(); + /// assert_eq!(array.last_node_mut().is_none(), true); + /// array.push(JsonValue::Null); + /// assert_eq!(array.last_node_mut().is_some(), true); + /// ``` + pub fn last_node_mut(&mut self) -> Option<&mut Node> { + self.inner.back_node_mut() + } + + /// Gets the common cursor of the specified index node. + fn get_cursor(&self, index: usize) -> Option> { + let len = self.len(); + // If index is greater than the array length, returns. + // If index is less than half the array length, searches from front to back; + // If index is greater than half the array length, searches from the back to the front. + return if index >= len { + None + } else if index >= (len - 1) / 2 { + let mut steps = len - 1 - index; + let mut cursor = self.inner.cursor_back(); + while steps != 0 { + let _ = cursor.index()?; + cursor.move_prev(); + steps -= 1; + } + Some(cursor) + } else { + let mut steps = index; + let mut cursor = self.inner.cursor_front(); + while steps != 0 { + let _ = cursor.index()?; + cursor.move_next(); + steps -= 1; + } + Some(cursor) + }; + } + + /// Gets the mutable cursor of the specified index node. + fn get_cursor_mut(&mut self, index: usize) -> Option> { + let len = self.len(); + // If index is greater than the array length, returns. + // If index is less than half the array length, searches from front to back; + // If index is greater than half the array length, searches from the back to the front. + return if index >= len { + None + } else if index >= (len - 1) / 2 { + let mut steps = len - 1 - index; + let mut cursor = self.inner.cursor_back_mut(); + while steps != 0 { + let _ = cursor.index()?; + cursor.move_prev(); + steps -= 1; + } + Some(cursor) + } else { + let mut steps = index; + let mut cursor = self.inner.cursor_front_mut(); + while steps != 0 { + let _ = cursor.index()?; + cursor.move_next(); + steps -= 1; + } + Some(cursor) + }; + } +} + +impl Display for Array { + fn fmt(&self, f: &mut Formatter<'_>) -> core::fmt::Result { + write!(f, "[")?; + for (n, item) in self.inner.iter().enumerate() { + if n != 0 { + write!(f, ",")?; + } + write!(f, "{item}")?; + } + write!(f, "]") + } +} + +impl Debug for Array { + fn fmt(&self, f: &mut Formatter<'_>) -> core::fmt::Result { + Display::fmt(self, f) + } +} + +#[cfg(test)] +mod ut_linked_list { + use crate::{Array, JsonValue}; + + /// UT test for `Array::is_empty`. + /// + /// # Title + /// ut_array_is_empty + /// + /// # Brief + /// 1. Creates some `Array`s. + /// 2. Calls `Array::is_empty`. + /// 3. Checks if the test results are correct. + #[test] + fn ut_array_is_empty() { + assert!(Array::new().is_empty()); + assert!(!array!(1).is_empty()); + } + + /// UT test for `Array::pop`. + /// + /// # Title + /// ut_array_pop + /// + /// # Brief + /// 1. Creates some `Array`s. + /// 2. Calls `Array::pop`. + /// 3. Checks if the test results are correct. + #[test] + fn ut_array_pop() { + let mut array = array!(1); + assert_eq!(array.pop(), Some(JsonValue::new_number(1.into()))); + assert_eq!(array.pop(), None); + } + + /// UT test for `Array::iter_mut`. + /// + /// # Title + /// ut_array_iter_mut + /// + /// # Brief + /// 1. Creates some `Array`s. + /// 2. Calls `Array::iter_mut`. + /// 3. Checks if the test results are correct. + #[test] + fn ut_array_iter_mut() { + let mut array = array!(1); + let mut iter = array.iter_mut(); + assert_eq!(iter.next(), Some(&mut JsonValue::new_number(1.into()))); + assert_eq!(iter.next(), None); + } + + /// UT test for `Array::last`. + /// + /// # Title + /// ut_array_last + /// + /// # Brief + /// 1. Creates some `Array`s. + /// 2. Calls `Array::last`. + /// 3. Checks if the test results are correct. + #[test] + fn ut_array_last() { + let array = array!(1); + assert_eq!(array.last(), Some(&JsonValue::new_number(1.into()))); + + let array = Array::new(); + assert_eq!(array.last(), None); + } + + /// UT test for `Array::get_node`. + /// + /// # Title + /// ut_array_get_node + /// + /// # Brief + /// 1. Creates some `Array`s. + /// 2. Calls `Array::get_node`. + /// 3. Checks if the test results are correct. + #[test] + fn ut_array_get_node() { + let array = array!(1, 2, 3, 4, 5, 6); + assert!(array.get_node(0).is_some()); + assert!(array.get_node(1).is_some()); + assert!(array.get_node(2).is_some()); + assert!(array.get_node(3).is_some()); + assert!(array.get_node(4).is_some()); + assert!(array.get_node(5).is_some()); + assert!(array.get_node(6).is_none()); + } + + /// UT test for `Array::get_node_mut`. + /// + /// # Title + /// ut_array_get_node_mut + /// + /// # Brief + /// 1. Creates some `Array`s. + /// 2. Calls `Array::get_node_mut`. + /// 3. Checks if the test results are correct. + #[test] + fn ut_array_get_node_mut() { + let mut array = array!(1, 2, 3, 4, 5, 6); + assert!(array.get_node_mut(0).is_some()); + assert!(array.get_node_mut(1).is_some()); + assert!(array.get_node_mut(2).is_some()); + assert!(array.get_node_mut(3).is_some()); + assert!(array.get_node_mut(4).is_some()); + assert!(array.get_node_mut(5).is_some()); + assert!(array.get_node_mut(6).is_none()); + } + + /// UT test for `Array::last_node`. + /// + /// # Title + /// ut_array_last_node + /// + /// # Brief + /// 1. Creates some `Array`s. + /// 2. Calls `Array::last_node`. + /// 3. Checks if the test results are correct. + #[test] + fn ut_array_last_node() { + let array = array!(1); + assert!(array.last_node().is_some()); + + let array = Array::new(); + assert!(array.last_node().is_none()); + } + + /// UT test for `Array::last_node_mut`. + /// + /// # Title + /// ut_array_last_node_mut + /// + /// # Brief + /// 1. Creates some `Array`s. + /// 2. Calls `Array::last_node_mut`. + /// 3. Checks if the test results are correct. + #[test] + fn ut_array_last_node_mut() { + let mut array = array!(1); + assert!(array.last_node_mut().is_some()); + + let mut array = Array::new(); + assert!(array.last_node_mut().is_none()); + } + + /// UT test for `Array::fmt`. + /// + /// # Title + /// ut_array_fmt + /// + /// # Brief + /// 1. Creates some `Array`s. + /// 2. Calls `Array::fmt`. + /// 3. Checks if the test results are correct. + #[test] + fn ut_array_fmt() { + let array = array!(1, 2); + assert_eq!(format!("{array}"), "[1,2]"); + assert_eq!(format!("{array:?}"), "[1,2]"); + } +} diff --git a/src/value/array/vec.rs b/src/value/array/vec.rs new file mode 100644 index 0000000000000000000000000000000000000000..429bb173ba5689949c872d087fbecd4080fd26c8 --- /dev/null +++ b/src/value/array/vec.rs @@ -0,0 +1,398 @@ +/* + * Copyright (c) 2023 Huawei Device Co., Ltd. + * Licensed 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. + */ + +use crate::JsonValue; +use core::fmt::{Debug, Display, Formatter}; +use core::slice::{Iter, IterMut}; + +/// Array type, implemented using Vec. +/// +/// # Situation +/// * When the average number of Array entries exceeds 15 (estimated value), and query operation exists. +/// +/// # Attention +/// * 只有开启 `vec_array` feature 时才可以使用,且与其他的 array 相关 feature 冲突。(默认开启) +/// Only open `vec_array` feature can be used, and conflicts with other array-related features. (Enabled by default) +/// +/// # Examples +/// ``` +/// use ylong_json::Array; +/// +/// let array = Array::new(); +/// assert_eq!(array.is_empty(), true); +/// ``` +#[derive(Default, Clone)] +pub struct Array { + inner: Vec, +} + +impl Array { + /// Creates an empty Array instance. + /// + /// # Examples + /// ``` + /// use ylong_json::Array; + /// + /// let array = Array::new(); + /// ``` + pub fn new() -> Self { + Self { inner: Vec::new() } + } + + /// Gets length of Array. + /// + /// # Examples + /// ``` + /// use ylong_json::{Array, JsonValue}; + /// + /// let mut array = Array::new(); + /// assert_eq!(array.len(), 0); + /// + /// array.push(JsonValue::Null); + /// assert_eq!(array.len(), 1); + /// ``` + pub fn len(&self) -> usize { + self.inner.len() + } + + /// Determines whether Array is empty. + /// + /// # Examples + /// ``` + /// use ylong_json::{Array, JsonValue}; + /// + /// let mut array = Array::new(); + /// assert_eq!(array.is_empty(), true); + /// + /// array.push(JsonValue::Null); + /// assert_eq!(array.is_empty(), false); + /// ``` + pub fn is_empty(&self) -> bool { + self.inner.len() == 0 + } + + /// Insert a new JsonValue at the end of Array. + /// + /// # Examples + /// ``` + /// use ylong_json::{Array, JsonValue}; + /// + /// let mut array = Array::new(); + /// assert_eq!(array.len(), 0); + /// + /// array.push(JsonValue::Null); + /// assert_eq!(array.len(), 1); + /// ``` + pub fn push(&mut self, value: JsonValue) { + self.inner.push(value) + } + + /// Pops the element at the end of Array. + /// + /// # Examples + /// ``` + /// use ylong_json::{Array, JsonValue}; + /// + /// let mut array = Array::new(); + /// assert_eq!(array.pop(), None); + /// + /// array.push(JsonValue::Null); + /// assert_eq!(array.pop(), Some(JsonValue::Null)); + /// ``` + pub fn pop(&mut self) -> Option { + self.inner.pop() + } + + /// Gets a common iterator of Array. + /// + /// # Examples + /// ``` + /// use ylong_json::Array; + /// + /// let array = Array::new(); + /// let iter = array.iter(); + /// ``` + pub fn iter(&self) -> Iter<'_, JsonValue> { + self.inner.iter() + } + + /// Gets a mutable iterator of Array. + /// + /// # Examples + /// ``` + /// use ylong_json::Array; + /// + /// let mut array = Array::new(); + /// let iter_mut = array.iter_mut(); + /// ``` + pub fn iter_mut(&mut self) -> IterMut<'_, JsonValue> { + self.inner.iter_mut() + } + + /// Returns a common reference to the specified index ** member ** in Array. + /// + /// # Examples + /// ``` + /// use ylong_json::{Array, JsonValue}; + /// + /// let mut array = Array::new(); + /// array.push(JsonValue::Null); + /// assert_eq!(array.get(0), Some(&JsonValue::Null)); + /// assert_eq!(array.get(1), None); + /// ``` + pub fn get(&self, index: usize) -> Option<&JsonValue> { + self.inner.get(index) + } + + /// Returns a mutable reference to the specified index ** member ** in Array. + /// + /// # Examples + /// ``` + /// use ylong_json::{Array, JsonValue}; + /// + /// let mut array = Array::new(); + /// array.push(JsonValue::Null); + /// assert_eq!(array.get_mut(0), Some(&mut JsonValue::Null)); + /// assert_eq!(array.get_mut(1), None); + /// ``` + pub fn get_mut(&mut self, index: usize) -> Option<&mut JsonValue> { + self.inner.get_mut(index) + } + + /// Returns a common reference to the trailing ** member ** in Array.。 + /// + /// # Examples + /// ``` + /// use ylong_json::{Array, JsonValue}; + /// + /// let mut array = Array::new(); + /// assert_eq!(array.last(), None); + /// array.push(JsonValue::Null); + /// assert_eq!(array.last(), Some(&JsonValue::Null)); + /// ``` + pub fn last(&self) -> Option<&JsonValue> { + self.inner.last() + } + + /// Returns a mutable reference to the trailing ** member ** in Array. + /// + /// # Examples + /// ``` + /// use ylong_json::{Array, JsonValue}; + /// + /// let mut array = Array::new(); + /// assert_eq!(array.last_mut(), None); + /// array.push(JsonValue::Null); + /// assert_eq!(array.last_mut(), Some(&mut JsonValue::Null)); + /// ``` + pub fn last_mut(&mut self) -> Option<&mut JsonValue> { + self.inner.last_mut() + } + + /// Removes the node in Array with the specified index. + /// + /// # Examples + /// ``` + /// use ylong_json::{Array, JsonValue}; + /// + /// let mut array = Array::new(); + /// array.push(JsonValue::Null); + /// array.push(JsonValue::Boolean(true)); + /// array.push(JsonValue::Null); + /// + /// assert_eq!(array.len(), 3); + /// let second = array.remove(1); + /// assert_eq!(second, Some(JsonValue::Boolean(true))); + /// assert_eq!(array.len(), 2); + /// ``` + pub fn remove(&mut self, index: usize) -> Option { + if index >= self.inner.len() { + return None; + } + Some(self.inner.remove(index)) + } +} + +impl PartialEq for Array { + /// Determines whether two arrays are equal. + /// + /// Two Arrays are equal: They have the same length and the elements in each position are equal. + /// + /// # Examples + /// ``` + /// use ylong_json::{Array, JsonValue}; + /// + /// let array1 = Array::new(); + /// let array2 = Array::new(); + /// let mut array3 = Array::new(); + /// array3.push(JsonValue::Null); + /// + /// assert_eq!(array1, array2); + /// assert_ne!(array1, array3); + /// ``` + fn eq(&self, other: &Self) -> bool { + if self.len() != other.len() { + return false; + } + for (a, b) in self.iter().zip(other.iter()) { + if a != b { + return false; + } + } + true + } +} + +impl Display for Array { + fn fmt(&self, f: &mut Formatter<'_>) -> core::fmt::Result { + write!(f, "[")?; + for (n, item) in self.inner.iter().enumerate() { + if n != 0 { + write!(f, ",")?; + } + write!(f, "{item}")?; + } + write!(f, "]") + } +} + +impl Debug for Array { + fn fmt(&self, f: &mut Formatter<'_>) -> core::fmt::Result { + Display::fmt(self, f) + } +} + +#[cfg(test)] +mod ut_vec { + use crate::{Array, JsonValue}; + + /// UT test for `Array::is_empty`. + /// + /// # Title + /// ut_array_is_empty + /// + /// # Brief + /// 1. Creates some `Array`s. + /// 2. Calls `Array::is_empty`. + /// 3. Checks if the test results are correct. + #[test] + fn ut_array_is_empty() { + assert!(Array::new().is_empty()); + assert!(!array!(1).is_empty()); + } + + /// UT test for `Array::pop`. + /// + /// # Title + /// ut_array_pop + /// + /// # Brief + /// 1. Creates some `Array`s. + /// 2. Calls `Array::pop`. + /// 3. Checks if the test results are correct. + #[test] + fn ut_array_pop() { + let mut array = array!(1); + assert_eq!(array.pop(), Some(JsonValue::new_number(1.into()))); + assert_eq!(array.pop(), None); + } + + /// UT test for `Array::iter_mut`. + /// + /// # Title + /// ut_array_iter_mut + /// + /// # Brief + /// 1. Creates some `Array`s. + /// 2. Calls `Array::iter_mut`. + /// 3. Checks if the test results are correct. + #[test] + fn ut_array_iter_mut() { + let mut array = array!(1); + let mut iter = array.iter_mut(); + assert_eq!(iter.next(), Some(&mut JsonValue::new_number(1.into()))); + assert_eq!(iter.next(), None); + } + + /// UT test for `Array::last`. + /// + /// # Title + /// ut_array_last + /// + /// # Brief + /// 1. Creates some `Array`s. + /// 2. Calls `Array::last`. + /// 3. Checks if the test results are correct. + #[test] + fn ut_array_last() { + let array = array!(1); + assert_eq!(array.last(), Some(&JsonValue::new_number(1.into()))); + + let array = Array::new(); + assert_eq!(array.last(), None); + } + + /// UT test for `Array::remove`. + /// + /// # Title + /// ut_array_remove + /// + /// # Brief + /// 1. Creates some `Array`s. + /// 2. Calls `Array::remove`. + /// 3. Checks if the test results are correct. + #[test] + fn ut_array_remove() { + let mut array = array!(1); + assert_eq!(array.remove(3), None); + assert_eq!(array.remove(0), Some(JsonValue::new_number(1.into()))); + } + + /// UT test for `Array::eq`. + /// + /// # Title + /// ut_array_eq + /// + /// # Brief + /// 1. Creates some `Array`s. + /// 2. Calls `Array::eq`. + /// 3. Checks if the test results are correct. + #[test] + fn ut_array_eq() { + let array1 = array!(1); + let array2 = array!(1, 2); + let array3 = array!(1, 3); + + assert_eq!(array1, array1); + assert_ne!(array1, array2); + assert_ne!(array2, array3); + } + + /// UT test for `Array::fmt`. + /// + /// # Title + /// ut_array_fmt + /// + /// # Brief + /// 1. Creates some `Array`s. + /// 2. Calls `Array::fmt`. + /// 3. Checks if the test results are correct. + #[test] + fn ut_array_fmt() { + let array = array!(1, 2); + assert_eq!(format!("{array}"), "[1,2]"); + assert_eq!(format!("{array:?}"), "[1,2]"); + } +} diff --git a/src/value/index.rs b/src/value/index.rs new file mode 100644 index 0000000000000000000000000000000000000000..434fe3dfe079ce1c4bf54bcebb58b1ae803f3408 --- /dev/null +++ b/src/value/index.rs @@ -0,0 +1,485 @@ +/* + * Copyright (c) 2023 Huawei Device Co., Ltd. + * Licensed 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. + */ + +use crate::{Array, JsonValue, Object}; + +/// Static NULL, which is returned if the searched key-value pair does not exist. +static NULL: JsonValue = JsonValue::Null; + +/// This trait can be used to get an index based on the subscript of an internal member of JsonValue. +pub trait Index: private::IndexSealed { + /// Gets a common reference to the value with the specified subscript (or key) from a JsonValue. + fn index_into<'a>(&self, value: &'a JsonValue) -> &'a JsonValue; + + /// Gets a mutable reference to the value of the specified subscript (or key) from a JsonValue. + fn index_into_mut<'a>(&self, value: &'a mut JsonValue) -> &'a mut JsonValue; + + /// Removes the member with the specified subscript (or key) from a JsonValue. + fn index_remove(&self, value: &mut JsonValue) -> Option; +} + +impl Index for usize { + /// Uses the array subscript to visit the Array type of JsonValue + /// and get a common reference to the corresponding JsonValue. + /// A null type will be returned in the following two cases: + /// + /// 1.Use a subscript to visit non-array types. + /// + /// 2.The subscript exceeds the current length of the Array type. + /// + /// # Examples + /// ``` + /// use ylong_json::{JsonValue, Array}; + /// + /// // Non-array types + /// assert_eq!(JsonValue::Number(0.0.into())[0], JsonValue::Null); + /// + /// // Array type + /// let mut array = Array::new(); + /// array.push(JsonValue::Null); + /// array.push(JsonValue::Boolean(true)); + /// array.push(JsonValue::Number(0.0.into())); + /// + /// let value = JsonValue::Array(array); + /// + /// // When subscript < length + /// assert_eq!(value[0], JsonValue::Null); + /// assert_eq!(value[1], JsonValue::Boolean(true)); + /// assert_eq!(value[2], JsonValue::Number(0.0.into())); + /// // When subscript >= length + /// assert_eq!(value[3], JsonValue::Null); + /// ``` + fn index_into<'a>(&self, value: &'a JsonValue) -> &'a JsonValue { + if let JsonValue::Array(ref array) = value { + if *self < array.len() { + return array.get(*self).unwrap(); + } + } + &NULL + } + + /// Uses the array subscript to visit the Array type of JsonValue + /// and get a mutable reference to the corresponding JsonValue. + /// + /// If the visited JsonValue is not Array type, the JsonValue will be + /// replaced with an empty Array type and visits again with that subscript. + /// + /// If the visited JsonValue is Array type, but the subscript exceeds the length of the array, + /// then adds a Null type JsonValue at the end of the array and return a mutable reference of it. + /// + /// # Examples + /// ``` + /// use ylong_json::{JsonValue, Array}; + /// + /// // Non-array types + /// let mut value = JsonValue::Null; + /// value[0] = JsonValue::Null; + /// + /// let mut array = Array::new(); + /// array.push(JsonValue::Null); + /// assert_eq!(value, JsonValue::Array(array)); + /// + /// // Array type + /// let mut array = Array::new(); + /// array.push(JsonValue::Null); + /// let mut value = JsonValue::Array(array); + /// + /// // Contains the subscript + /// value[0] = JsonValue::Number(0.0.into()); + /// assert_eq!(value[0], JsonValue::Number(0.0.into())); + /// assert_eq!(value.try_as_array().unwrap().len(), 1); + /// + /// // Does not contain the subscript + /// value[1] = JsonValue::Boolean(true); + /// assert_eq!(value[1], JsonValue::Boolean(true)); + /// assert_eq!(value.try_as_array().unwrap().len(), 2); + /// ``` + fn index_into_mut<'a>(&self, value: &'a mut JsonValue) -> &'a mut JsonValue { + if let JsonValue::Array(ref mut array) = value { + return if *self < array.len() { + array.get_mut(*self).unwrap() + } else { + array.push(JsonValue::Null); + array.last_mut().unwrap() + }; + } + *value = JsonValue::new_array(Array::new()); + self.index_into_mut(value) + } + + /// Removes the element at the specified location of Array type JsonValue and returns that content. + /// + /// # Examples + /// ``` + /// use ylong_json::{JsonValue, Array}; + /// + /// let mut array = Array::new(); + /// array.push(1i32.into()); + /// + /// let mut value: JsonValue = JsonValue::Array(array); + /// assert_eq!(value[0], 1i32.into()); + /// + /// let ret = value.remove(0); + /// assert_eq!(value[0], JsonValue::Null); + /// assert_eq!(ret.unwrap(), 1i32.into()); + /// ``` + fn index_remove(&self, value: &mut JsonValue) -> Option { + if let JsonValue::Array(ref mut array) = value { + if *self < array.len() { + return array.remove(*self); + } + } + None + } +} + +impl Index for str { + /// Uses key to visit Object type JsonValue, and returns a common reference to corresponding JsonValue. + /// A null type will be returned in the following two cases: + /// + /// 1.Uses key to visit non-object types. + /// + /// 2.The searched Object type does not contain the key. + /// + /// # Examples + /// ``` + /// use ylong_json::{JsonValue, Object}; + /// + /// // Non-object types + /// assert_eq!(JsonValue::Number(0.0.into())["key"], JsonValue::Null); + /// + /// // Object type + /// let mut object = Object::new(); + /// object.insert(String::from("key"), JsonValue::Number(0.0.into())); + /// + /// let value = JsonValue::Object(object); + /// + /// // The key exists. + /// assert_eq!(value["key"], JsonValue::Number(0.0.into())); + /// + /// // The key does not exist. + /// assert_eq!(value["not exist"], JsonValue::Null); + /// ``` + fn index_into<'a>(&self, value: &'a JsonValue) -> &'a JsonValue { + if let JsonValue::Object(ref object) = value { + return object.get(self).unwrap_or(&NULL); + } + &NULL + } + + /// Uses key to visit Object type JsonValue, and returns a mutable reference to corresponding JsonValue. + /// + /// If the visited JsonValue is not Object type, the JsonValue will be + /// replaced with an empty Object type and visits again with that key. + /// + /// If the visited JsonValue is of object type but does not contain the key, a key-value pair of + /// the key and a null type will be inserted and returns a mutable reference to the JsonValue. + /// + /// + /// # Examples + /// ``` + /// use ylong_json::{JsonValue, Object}; + /// + /// // Non-object types + /// let mut value = JsonValue::Null; + /// let mut object = Object::new(); + /// object.insert(String::from("key"), JsonValue::Number(0.0.into())); + /// + /// value["key"] = JsonValue::Number(0.0.into()); + /// assert_eq!(value, JsonValue::Object(object)); + /// + /// // Object type + /// let mut object = Object::new(); + /// object.insert(String::from("key"), JsonValue::Number(0.0.into())); + /// let mut value = JsonValue::Object(object); + /// + /// // Contains the key. + /// value["key"] = JsonValue::Boolean(true); + /// assert_eq!(value["key"], JsonValue::Boolean(true)); + /// assert_eq!(value.try_as_mut_object().unwrap().len(), 1); + /// + /// // Dose not contain the key. + /// value["not exist"] = JsonValue::Number(1.1.into()); + /// assert_eq!(value["not exist"], JsonValue::Number(1.1.into())); + /// assert_eq!(value.try_as_mut_object().unwrap().len(), 2); + /// ``` + fn index_into_mut<'a>(&self, value: &'a mut JsonValue) -> &'a mut JsonValue { + if let JsonValue::Object(ref mut object) = value { + #[cfg(any(feature = "list_object"))] + { + return object.get_key_mut_maybe_insert(self); + } + #[cfg(feature = "vec_object")] + { + if let Some(pos) = object.iter().position(|(k, _)| k == self) { + return object.get_mut_by_position(pos).unwrap(); + } + object.insert(String::from(self), JsonValue::Null); + return object.last_mut().unwrap(); + } + #[cfg(feature = "btree_object")] + { + if !object.contains_key(self) { + object.insert(String::from(self), JsonValue::Null); + } + return object.get_mut(self).unwrap(); + } + } + *value = JsonValue::Object(Object::new()); + self.index_into_mut(value) + } + + /// Removes the element at the specified location of Object type JsonValue and returns that content. + /// + /// # Examples + /// ``` + /// use ylong_json::{Object, JsonValue}; + /// + /// let mut object = Object::new(); + /// object.insert(String::from("key"), "value".into()); + /// + /// let mut value: JsonValue = object.into(); + /// assert_eq!(value["key"], "value".into()); + /// + /// let ret = value.remove("key"); + /// assert_eq!(value["key"], JsonValue::Null); + /// assert_eq!(ret.unwrap(), "value".into()); + /// ``` + fn index_remove(&self, value: &mut JsonValue) -> Option { + if let JsonValue::Object(ref mut object) = value { + return object.remove(self); + } + None + } +} + +impl Index for String { + /// Same as 'Index for str'. + fn index_into<'a>(&self, value: &'a JsonValue) -> &'a JsonValue { + self.as_str().index_into(value) + } + + /// Same as 'Index for str'. + fn index_into_mut<'a>(&self, value: &'a mut JsonValue) -> &'a mut JsonValue { + self.as_str().index_into_mut(value) + } + + /// Same as 'Index for str'. + fn index_remove(&self, value: &mut JsonValue) -> Option { + self.as_str().index_remove(value) + } +} + +impl<'a, T> Index for &'a T +where + T: ?Sized + Index, +{ + /// Implements Index for the relevant reference type. + fn index_into<'v>(&self, value: &'v JsonValue) -> &'v JsonValue { + (**self).index_into(value) + } + + /// Implements Index for the relevant reference type. + fn index_into_mut<'v>(&self, value: &'v mut JsonValue) -> &'v mut JsonValue { + (**self).index_into_mut(value) + } + + /// Implements Index for the relevant reference type. + fn index_remove(&self, value: &mut JsonValue) -> Option { + (**self).index_remove(value) + } +} + +// To prevent the Index by external implementation. +mod private { + pub trait IndexSealed {} + + impl IndexSealed for usize {} + + impl IndexSealed for str {} + + impl IndexSealed for String {} + + impl<'a, T> IndexSealed for &'a T where T: ?Sized + IndexSealed {} +} + +#[cfg(test)] +mod ut_index { + use crate::{Array, Index, JsonValue, Object}; + + /// UT test for `usize::index_into`. + /// + /// # Title + /// ut_usize_index_into + /// + /// # Brief + /// 1. Creates some `usize`s and some `JsonValue`s. + /// 2. Calls `Index::index_into`. + /// 3. Checks if the test results are correct. + #[test] + fn ut_usize_index_into() { + let value = JsonValue::new_boolean(true); + assert!(1usize.index_into(&value).is_null()); + } + + /// UT test for `usize::index_into_mut`. + /// + /// # Title + /// ut_usize_index_into_mut + /// + /// # Brief + /// 1. Creates some `usize`s and some `JsonValue`s. + /// 2. Calls `Index::index_into_mut`. + /// 3. Checks if the test results are correct. + #[test] + fn ut_usize_index_into_mut() { + let mut value = JsonValue::new_array(array!(1)); + assert!(0usize.index_into_mut(&mut value).is_number()); + assert!(1usize.index_into_mut(&mut value).is_null()); + + let mut value = JsonValue::new_null(); + assert!(0usize.index_into_mut(&mut value).is_null()); + assert!(value.is_array()) + } + + /// UT test for `usize::index_remove`. + /// + /// # Title + /// ut_usize_index_remove + /// + /// # Brief + /// 1. Creates some `usize`s and some `JsonValue`s. + /// 2. Calls `Index::index_remove`. + /// 3. Checks if the test results are correct. + #[test] + fn ut_usize_index_remove() { + let mut value = JsonValue::new_array(array!(1)); + assert_eq!( + 0usize.index_remove(&mut value), + Some(JsonValue::new_number(1.into())) + ); + assert!(0usize.index_remove(&mut value).is_none()); + } + + /// UT test for `str::index_into`. + /// + /// # Title + /// ut_str_index_into + /// + /// # Brief + /// 1. Creates some `str`s and some `JsonValue`s. + /// 2. Calls `Index::index_into`. + /// 3. Checks if the test results are correct. + #[test] + fn ut_str_index_into() { + let value = JsonValue::new_boolean(true); + assert!("key".index_into(&value).is_null()); + } + + /// UT test for `str::index_into_mut`. + /// + /// # Title + /// ut_str_index_into_mut + /// + /// # Brief + /// 1. Creates some `str`s and some `JsonValue`s. + /// 2. Calls `Index::index_into_mut`. + /// 3. Checks if the test results are correct. + #[test] + fn ut_str_index_into_mut() { + let mut value = JsonValue::new_object(object!("key1" => "value1")); + assert!("key1".index_into_mut(&mut value).is_string()); + assert!("key2".index_into_mut(&mut value).is_null()); + + let mut value = JsonValue::new_null(); + assert!("key1".index_into_mut(&mut value).is_null()); + assert!(value.is_object()) + } + + /// UT test for `str::index_remove`. + /// + /// # Title + /// ut_str_index_remove + /// + /// # Brief + /// 1. Creates some `str`s and some `JsonValue`s. + /// 2. Calls `Index::index_remove`. + /// 3. Checks if the test results are correct. + #[test] + fn ut_str_index_remove() { + let mut value = JsonValue::new_object(object!("key1" => "value1")); + assert_eq!( + "key1".index_remove(&mut value), + Some(JsonValue::new_string("value1")) + ); + + let mut value = JsonValue::new_null(); + assert!("key1".index_remove(&mut value).is_none()); + } + + /// UT test for `String::index_into`. + /// + /// # Title + /// ut_string_index_into + /// + /// # Brief + /// 1. Creates some `String`s and some `JsonValue`s. + /// 2. Calls `Index::index_into`. + /// 3. Checks if the test results are correct. + #[test] + fn ut_string_index_into() { + let value = JsonValue::new_boolean(true); + assert!(String::from("key").index_into(&value).is_null()); + } + + /// UT test for `String::index_into_mut`. + /// + /// # Title + /// ut_string_index_into_mut + /// + /// # Brief + /// 1. Creates some `String`s and some `JsonValue`s. + /// 2. Calls `Index::index_into_mut`. + /// 3. Checks if the test results are correct. + #[test] + fn ut_string_index_into_mut() { + let mut value = JsonValue::new_object(object!("key1" => "value1")); + assert!(String::from("key1").index_into_mut(&mut value).is_string()); + assert!(String::from("key2").index_into_mut(&mut value).is_null()); + + let mut value = JsonValue::new_null(); + assert!(String::from("key1").index_into_mut(&mut value).is_null()); + assert!(value.is_object()) + } + + /// UT test for `String::index_remove`. + /// + /// # Title + /// ut_string_index_remove + /// + /// # Brief + /// 1. Creates some `String`s and some `JsonValue`s. + /// 2. Calls `Index::index_remove`. + /// 3. Checks if the test results are correct. + #[test] + fn ut_string_index_remove() { + let mut value = JsonValue::new_object(object!("key1" => "value1")); + assert_eq!( + String::from("key1").index_remove(&mut value), + Some(JsonValue::new_string("value1")) + ); + assert!(String::from("key1").index_remove(&mut value).is_none()); + } +} diff --git a/src/value/number.rs b/src/value/number.rs new file mode 100644 index 0000000000000000000000000000000000000000..577c96a10b84148419b507c2d44c45a25026a194 --- /dev/null +++ b/src/value/number.rs @@ -0,0 +1,348 @@ +/* + * Copyright (c) 2023 Huawei Device Co., Ltd. + * Licensed 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. + */ + +use crate::Error; +use core::fmt::{Display, Formatter}; +use std::fmt::Debug; + +/// Numerical type +/// +/// # Examples +/// ``` +/// use ylong_json::Number; +/// +/// let number: Number = 0.0.into(); +/// assert_eq!(number.is_float(), true); +/// ``` +#[derive(Clone)] +pub enum Number { + /// Unsigned integer + Unsigned(u64), + /// Signed integer + Signed(i64), + /// Floating point number + Float(f64), +} + +impl Number { + /// Determines whether the number is an unsigned integer. + /// + /// # Examples + /// ``` + /// use ylong_json::Number; + /// + /// let number: Number = 1u8.into(); + /// assert_eq!(number.is_unsigned(), true); + /// + /// let number: Number = 1i8.into(); + /// assert_eq!(number.is_unsigned(), false); + /// ``` + pub fn is_unsigned(&self) -> bool { + matches!(*self, Self::Unsigned(_)) + } + + /// Determines whether the number is a signed integer. + /// + /// # Examples + /// ``` + /// use ylong_json::Number; + /// + /// let number: Number = 1i8.into(); + /// assert_eq!(number.is_signed(), true); + /// + /// let number: Number = 1u8.into(); + /// assert_eq!(number.is_signed(), false); + /// ``` + pub fn is_signed(&self) -> bool { + matches!(*self, Self::Signed(_)) + } + + /// Determines whether the number is a floating point number. + /// + /// # Examples + /// ``` + /// use ylong_json::Number; + /// + /// let number: Number = 0.0.into(); + /// assert_eq!(number.is_float(), true); + /// + /// let number: Number = 1i8.into(); + /// assert_eq!(number.is_float(), false); + /// ``` + pub fn is_float(&self) -> bool { + matches!(*self, Self::Float(_)) + } + + /// Trys converting the number to u64. If conversion fails, returns Error. + /// + /// Only Unsigned case means success, other cases return Error. + /// + /// # Examples + /// ``` + /// use ylong_json::Number; + /// + /// let number: Number = 1u8.into(); + /// assert_eq!(number.try_as_u64().unwrap(), 1u64); + /// + /// let number: Number = 1i8.into(); + /// assert_eq!(number.try_as_u64().is_err(), true); + /// ``` + pub fn try_as_u64(&self) -> Result { + match self { + Self::Unsigned(u) => Ok(*u), + _ => Err(Error::TypeTransform), + } + } + + /// Trys converting the number to i64. If conversion fails, returns Error. + /// + /// Only in 164 range unsigned numbers can be converted. Signed numbers can be converted. + /// Otherwise, returns Error. + /// + /// # Examples + /// ``` + /// use ylong_json::Number; + /// + /// let number: Number = 1i8.into(); + /// assert_eq!(number.try_as_i64().unwrap(), 1i64); + /// + /// let number: Number = u64::MAX.into(); + /// assert_eq!(number.try_as_i64().is_err(), true); + /// ``` + pub fn try_as_i64(&self) -> Result { + match self { + Self::Unsigned(u) => { + if *u <= i64::MAX as u64 { + Ok(*u as i64) + } else { + Err(Error::TypeTransform) + } + } + Self::Signed(i) => Ok(*i), + Self::Float(_) => Err(Error::TypeTransform), + } + } + + /// Trys converting the number to f64. If conversion fails, returns Error. + /// + /// All types can be converted to f64. + /// + /// # Examples + /// ``` + /// use ylong_json::Number; + /// + /// let number: Number = 0.0.into(); + /// assert_eq!(number.try_as_f64().unwrap(), 0.0f64); + /// ``` + pub fn try_as_f64(&self) -> Result { + match self { + Self::Unsigned(u) => Ok(*u as f64), + Self::Signed(i) => Ok(*i as f64), + Self::Float(f) => Ok(*f), + } + } +} + +impl PartialEq for Number { + fn eq(&self, other: &Self) -> bool { + let a = self.try_as_f64().unwrap(); + let b = other.try_as_f64().unwrap(); + a == b + } +} + +impl Display for Number { + fn fmt(&self, f: &mut Formatter<'_>) -> core::fmt::Result { + match self { + Number::Unsigned(x) => write!(f, "{x}"), + Number::Signed(x) => write!(f, "{x}"), + Number::Float(x) => write!(f, "{x:?}"), + } + } +} + +impl Debug for Number { + fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result { + Display::fmt(self, f) + } +} + +macro_rules! number_from_unsigned { + ($($u: tt),* $(,)?) => { + $( + impl From<$u> for Number { + fn from(u: $u) -> Self { + Self::Unsigned(u as u64) + } + } + )* + } +} + +macro_rules! number_from_signed { + ($($i: tt),* $(,)?) => { + $( + impl From<$i> for Number { + fn from(i: $i) -> Self { + Self::Signed(i as i64) + } + } + )* + } +} + +macro_rules! number_from_float { + ($($f: tt),* $(,)?) => { + $( + impl From<$f> for Number { + fn from(f: $f) -> Self { + Self::Float(f as f64) + } + } + )* + } +} + +number_from_unsigned!(u8, u16, u32, u64, usize); +number_from_signed!(i8, i16, i32, i64, isize); +number_from_float!(f32, f64); + +#[cfg(test)] +mod ut_number { + use crate::Number; + + /// UT test for `Number::fmt`. + /// + /// # Title + /// ut_number_fmt + /// + /// # Brief + /// 1. Creates some `Number`s. + /// 2. Calls `Number::fmt`. + /// 3. Checks if the test results are correct. + #[test] + fn ut_number_fmt() { + assert_eq!(format!("{}", Number::Unsigned(1)), "1"); + assert_eq!(format!("{:?}", Number::Unsigned(1)), "1"); + + assert_eq!(format!("{}", Number::Signed(1)), "1"); + assert_eq!(format!("{:?}", Number::Signed(1)), "1"); + + assert_eq!(format!("{}", Number::Float(1.0)), "1.0"); + assert_eq!(format!("{:?}", Number::Float(1.0)), "1.0"); + } + + /// UT test for `Number::clone`. + /// + /// # Title + /// ut_number_clone + /// + /// # Brief + /// 1. Creates some `Number`s. + /// 2. Calls `Number::clone`. + /// 3. Checks if the test results are correct. + #[test] + fn ut_number_clone() { + let number1 = Number::Unsigned(1); + assert_eq!(number1, number1.clone()); + + let number1 = Number::Signed(1); + assert_eq!(number1, number1.clone()); + + let number1 = Number::Float(1.0); + assert_eq!(number1, number1.clone()); + } + + /// UT test for `Number::is_unsigned`. + /// + /// # Title + /// ut_number_is_unsigned + /// + /// # Brief + /// 1. Creates some `Number`s. + /// 2. Calls `Number::is_unsigned`. + /// 3. Checks if the test results are correct. + #[test] + fn ut_number_is_unsigned() { + assert!(Number::Unsigned(1).is_unsigned()); + assert!(!Number::Signed(1).is_unsigned()); + assert!(!Number::Float(1.0).is_unsigned()); + } + + /// UT test for `Number::is_signed`. + /// + /// # Title + /// ut_number_is_signed + /// + /// # Brief + /// 1. Creates some `Number`s. + /// 2. Calls `Number::is_signed`. + /// 3. Checks if the test results are correct. + #[test] + fn ut_number_is_signed() { + assert!(!Number::Unsigned(1).is_signed()); + assert!(Number::Signed(1).is_signed()); + assert!(!Number::Float(1.0).is_signed()); + } + + /// UT test for `Number::is_float`. + /// + /// # Title + /// ut_number_is_float + /// + /// # Brief + /// 1. Creates some `Number`s. + /// 2. Calls `Number::is_float`. + /// 3. Checks if the test results are correct. + #[test] + fn ut_number_is_float() { + assert!(!Number::Unsigned(1).is_float()); + assert!(!Number::Signed(1).is_float()); + assert!(Number::Float(1.0).is_float()); + } + + /// UT test for `Number::try_as_u64`. + /// + /// # Title + /// ut_number_try_as_u64 + /// + /// # Brief + /// 1. Creates some `Number`s. + /// 2. Calls `Number::try_as_u64`. + /// 3. Checks if the test results are correct. + #[test] + fn ut_number_try_as_u64() { + assert!(Number::Unsigned(1).try_as_u64().is_ok()); + assert!(Number::Signed(1).try_as_u64().is_err()); + assert!(Number::Float(1.0).try_as_u64().is_err()); + } + + /// UT test for `Number::try_as_i64`. + /// + /// # Title + /// ut_number_try_as_i64 + /// + /// # Brief + /// 1. Creates some `Number`s. + /// 2. Calls `Number::try_as_i64`. + /// 3. Checks if the test results are correct. + #[test] + fn ut_number_try_as_i64() { + assert!(Number::Unsigned(1).try_as_i64().is_ok()); + assert!(Number::Unsigned(u64::MAX).try_as_i64().is_err()); + assert!(Number::Signed(1).try_as_i64().is_ok()); + assert!(Number::Float(1.0).try_as_i64().is_err()); + } +} diff --git a/src/value/object.rs b/src/value/object.rs new file mode 100644 index 0000000000000000000000000000000000000000..1d2f4fc2184d447bc538f6b8127ed568b69109d0 --- /dev/null +++ b/src/value/object.rs @@ -0,0 +1,29 @@ +/* + * Copyright (c) 2023 Huawei Device Co., Ltd. + * Licensed 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. + */ + +#[cfg(feature = "btree_object")] +mod btree; +#[cfg(feature = "btree_object")] +pub use btree::Object; + +#[cfg(feature = "list_object")] +mod linked_list; +#[cfg(feature = "list_object")] +pub use linked_list::Object; + +#[cfg(feature = "vec_object")] +mod vec; +#[cfg(feature = "vec_object")] +pub use vec::Object; diff --git a/src/value/object/btree.rs b/src/value/object/btree.rs new file mode 100644 index 0000000000000000000000000000000000000000..dbc7c506e1b080d539f4b5bee65c6b7eb3940682 --- /dev/null +++ b/src/value/object/btree.rs @@ -0,0 +1,279 @@ +/* + * Copyright (c) 2023 Huawei Device Co., Ltd. + * Licensed 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. + */ + +use crate::JsonValue; +use core::fmt::{Debug, Display, Formatter}; +use std::collections::btree_map::{BTreeMap, Iter, IterMut}; + +/// Object type, implemented using the standard library Btree. +/// +/// # Situation +/// * When the average number of objects exceeds 1024 (estimated value) but does not exceed 5000 (estimated value), +/// and the creation and query ratio is greater than 600.(Number of queries for 1 Object creation). +/// +/// * When the average number of objects exceeds 5000 (estimated value). +/// +/// # Attention +/// Only opening ` btree_object ` feature can be used, and associated with the object of other feature conflict. (Enabled by default) +/// +/// # Examples +/// ``` +/// use ylong_json::Object; +/// +/// let object = Object::new(); +/// assert_eq!(object.is_empty(), true); +/// ``` +#[derive(Default, Clone, PartialEq)] +pub struct Object { + inner: BTreeMap, +} + +impl Object { + /// Creates an empty Object. + /// + /// # Examples + /// ``` + /// use ylong_json::Object; + /// + /// let object = Object::new(); + /// assert_eq!(object.is_empty(), true); + /// ``` + pub fn new() -> Self { + Self { + inner: BTreeMap::new(), + } + } + + /// Gets the length of Object. + /// + /// # Examples + /// ``` + /// use ylong_json::{JsonValue, Object}; + /// + /// let mut object = Object::new(); + /// assert_eq!(object.len(), 0); + /// object.insert(String::from("null"), JsonValue::Null); + /// assert_eq!(object.len(), 1); + /// ``` + pub fn len(&self) -> usize { + self.inner.len() + } + + /// Determines whether the Object is empty. + /// + /// # Examples + /// ``` + /// use ylong_json::{JsonValue, Object}; + /// + /// let mut object = Object::new(); + /// assert_eq!(object.is_empty(), true); + /// object.insert(String::from("null"), JsonValue::Null); + /// assert_eq!(object.is_empty(), false); + /// ``` + pub fn is_empty(&self) -> bool { + self.inner.is_empty() + } + + /// Checks whether the specified key exists in the Object. + /// + /// # Examples + /// ``` + /// use ylong_json::{JsonValue, Object, Number}; + /// + /// let mut object = Object::new(); + /// object.insert(String::from("null"), JsonValue::Null); + /// + /// assert_eq!(object.contains_key("null"), true); + /// assert_eq!(object.contains_key("no_such_key"), false); + /// ``` + pub fn contains_key(&self, key: &str) -> bool { + self.inner.contains_key(key) + } + + /// Inserts the specified key and value into the Object, and replaces the value if the key already exists in the Object. + /// + /// # Examples + /// ``` + /// use ylong_json::{JsonValue, Object}; + /// + /// let mut object = Object::new(); + /// assert_eq!(object.len(), 0); + /// object.insert(String::from("null"), JsonValue::Null); + /// assert_eq!(object.len(), 1); + /// ``` + pub fn insert(&mut self, key: String, value: JsonValue) { + self.inner.insert(key, value); + } + + /// Removes the element under the specified Key from Object. + /// + /// # Examples + /// ``` + /// use ylong_json::{JsonValue, Object, Number}; + /// + /// let mut object = Object::new(); + /// object.insert(String::from("null"), JsonValue::Null); + /// assert_eq!(object.len(), 1); + /// assert_eq!(object.remove("null"), Some(JsonValue::Null)); + /// assert_eq!(object.len(), 0); + /// ``` + pub fn remove(&mut self, key: &str) -> Option { + self.inner.remove(key) + } + + /// Gets a common iterator of Object. + /// + /// # Examples + /// ``` + /// use ylong_json::Object; + /// + /// let object = Object::new(); + /// let iter = object.iter(); + /// ``` + pub fn iter(&self) -> Iter<'_, String, JsonValue> { + self.inner.iter() + } + + /// Gets a mutable iterator of Object. + /// + /// # Examples + /// ``` + /// use ylong_json::Object; + /// + /// let mut object = Object::new(); + /// let iter_mut = object.iter_mut(); + /// ``` + pub fn iter_mut(&mut self) -> IterMut<'_, String, JsonValue> { + self.inner.iter_mut() + } + + /// Gets a common reference to the element in Object with the specified key. + /// + /// # Examples + /// ``` + /// use ylong_json::{JsonValue, Object, Number}; + /// + /// let mut object = Object::new(); + /// object.insert(String::from("test"), JsonValue::Number(Number::from(123))); + /// + /// assert_eq!(object.get("test"), Some(&JsonValue::Number(Number::from(123)))); + /// assert_eq!(object.get("no_such_key"), None); + /// ``` + pub fn get(&self, key: &str) -> Option<&JsonValue> { + self.inner.get(key) + } + + /// Gets a mutable reference to the element in Object with the specified key. + /// + /// # Examples + /// ``` + /// use ylong_json::{JsonValue, Object}; + /// + /// let mut object = Object::new(); + /// object.insert(String::from("null"), JsonValue::Null); + /// + /// assert_eq!(object.get_mut("null"), Some(&mut JsonValue::Null)); + /// assert_eq!(object.get_mut("no_such_key"), None); + /// ``` + pub fn get_mut(&mut self, key: &str) -> Option<&mut JsonValue> { + self.inner.get_mut(key) + } +} + +impl Display for Object { + fn fmt(&self, f: &mut Formatter<'_>) -> core::fmt::Result { + write!(f, "{{")?; + for (n, (key, value)) in self.inner.iter().enumerate() { + if n != 0 { + write!(f, ",")?; + } + write!(f, "\"{key}\":{value}")?; + } + write!(f, "}}") + } +} + +impl Debug for Object { + fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result { + Display::fmt(self, f) + } +} + +#[cfg(test)] +mod ut_btree { + use crate::{JsonValue, Object}; + + /// UT test for `Object::iter_mut`. + /// + /// # Title + /// ut_object_iter_mut + /// + /// # Brief + /// 1. Creates some `Object`s. + /// 2. Calls `Object::iter_mut`. + /// 3. Checks if the test results are correct. + #[test] + fn ut_object_iter_mut() { + let mut object = object!("key1" => "value1"); + let mut iter = object.iter_mut(); + assert_eq!( + iter.next(), + Some((&String::from("key1"), &mut JsonValue::new_string("value1"))) + ); + assert_eq!(iter.next(), None); + } + + /// UT test for `Object::fmt`. + /// + /// # Title + /// ut_object_fmt + /// + /// # Brief + /// 1. Creates a `Object`. + /// 2. Calls `Object::fmt` on it. + /// 3. Checks if the test results are correct. + #[test] + fn ut_object_fmt() { + let object = object!("key1" => "value1"; "key2" => "value2"); + assert_eq!( + format!("{object}"), + "{\"key1\":\"value1\",\"key2\":\"value2\"}" + ); + assert_eq!( + format!("{object:?}"), + "{\"key1\":\"value1\",\"key2\":\"value2\"}" + ); + } + + /// UT test for `Object::eq`. + /// + /// # Title + /// ut_object_fmt + /// + /// # Brief + /// 1. Creates a `Object`. + /// 2. Calls `Object::eq` on it. + /// 3. Checks if the test results are correct. + #[test] + fn ut_object_eq() { + let object1 = object!("key1" => "value1"); + let object2 = object!("key1" => "value1"; "key2" => "value2"); + let object3 = object!("key1" => "value1"; "key3" => "value3"); + + assert_eq!(object1, object1); + assert_ne!(object1, object2); + assert_ne!(object2, object3); + } +} diff --git a/src/value/object/linked_list.rs b/src/value/object/linked_list.rs new file mode 100644 index 0000000000000000000000000000000000000000..90439484088bc904f905e395f09ea1649f8a0e9e --- /dev/null +++ b/src/value/object/linked_list.rs @@ -0,0 +1,430 @@ +/* + * Copyright (c) 2023 Huawei Device Co., Ltd. + * Licensed 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. + */ + +use crate::{Cursor, CursorMut, Iter, IterMut, JsonValue, LinkedList, Node}; +use core::fmt::{Debug, Display, Formatter}; +use core::ptr::null; + +/// Object type, implemented using LinkedList. +/// +/// # Situation +/// * When the average number of items under Object is less than 15 (estimated value). +/// +/// * When the average number of items under Object exceeds 15 (estimated), but do not or rarely made the query operation. +/// +/// # Attention +/// * Only opening the 'list_object' feature, this Object type can be used , and it conflicts with other objects. +/// +/// * This Object ** does not provide the ** de-duplicate function. +/// * Users are required to ensure that there are no duplicate entries. +/// +/// * The output order of this Object is the same as the insertion order. +/// +/// # Examples +/// ``` +/// use ylong_json::Object; +/// +/// let object = Object::new(); +/// ``` +#[derive(Default, Clone, PartialEq)] +pub struct Object { + inner: LinkedList<(String, JsonValue)>, +} + +impl Object { + /// Creates an empty Object。 + /// + /// # Examples + /// ``` + /// use ylong_json::Object; + /// + /// let object = Object::new(); + /// assert_eq!(object.is_empty(), true); + /// ``` + pub fn new() -> Self { + Self { + inner: LinkedList::new(), + } + } + + /// Gets the length of Object. + /// + /// # Examples + /// ``` + /// use ylong_json::{JsonValue, Object}; + /// + /// let mut object = Object::new(); + /// assert_eq!(object.len(), 0); + /// object.insert(String::from("null"), JsonValue::Null); + /// assert_eq!(object.len(), 1); + /// ``` + pub fn len(&self) -> usize { + self.inner.len() + } + + /// Determines whether the Object is empty. + /// + /// # Examples + /// ``` + /// use ylong_json::{JsonValue, Object}; + /// + /// let mut object = Object::new(); + /// assert_eq!(object.is_empty(), true); + /// object.insert(String::from("null"), JsonValue::Null); + /// assert_eq!(object.is_empty(), false); + /// ``` + pub fn is_empty(&self) -> bool { + self.inner.is_empty() + } + + /// Checks whether the specified key exists in the Object. + /// + /// # Examples + /// ``` + /// use ylong_json::{JsonValue, Object, Number}; + /// + /// let mut object = Object::new(); + /// object.insert(String::from("null"), JsonValue::Null); + /// + /// assert_eq!(object.contains_key("null"), true); + /// assert_eq!(object.contains_key("no_such_key"), false); + /// ``` + pub fn contains_key(&self, key: &str) -> bool { + self.get_cursor(key).is_some() + } + + /// Inserts the specified key and value into an Object, appending them to the end without deduplication. + /// + /// # Examples + /// ``` + /// use ylong_json::{JsonValue, Object}; + /// + /// let mut object = Object::new(); + /// assert_eq!(object.len(), 0); + /// object.insert(String::from("null"), JsonValue::Null); + /// assert_eq!(object.len(), 1); + /// ``` + pub fn insert(&mut self, key: String, value: JsonValue) { + self.inner.push_back((key, value)) + } + + /// Removes the element under the specified key from the Object.If there is an element with + /// the same name in the Object, deletes the one with the smallest subscript. + /// + /// # Examples + /// ``` + /// use ylong_json::{JsonValue, Object, Number}; + /// + /// let mut object = Object::new(); + /// object.insert(String::from("null"), JsonValue::Null); + /// assert_eq!(object.len(), 1); + /// assert_eq!(object.remove("null"), Some(JsonValue::Null)); + /// assert_eq!(object.len(), 0); + /// ``` + pub fn remove(&mut self, key: &str) -> Option { + self.get_cursor_mut(key)?.remove_current().map(|(_, v)| v) + } + + /// Gets a common iterator of Object. + /// + /// # Examples + /// ``` + /// use ylong_json::Object; + /// + /// let object = Object::new(); + /// let iter = object.iter(); + /// ``` + pub fn iter(&self) -> Iter<'_, (String, JsonValue)> { + self.inner.iter() + } + + /// Gets a mutable iterator of Object. + /// + /// # Examples + /// ``` + /// use ylong_json::Object; + /// + /// let mut object = Object::new(); + /// let iter_mut = object.iter_mut(); + /// ``` + pub fn iter_mut(&mut self) -> IterMut<'_, (String, JsonValue)> { + self.inner.iter_mut() + } + + /// Gets a common reference to the element in Object with the specified key. + /// If there is an element with the same name, returns the one with the smallest subscript. + /// + /// # Examples + /// ``` + /// use ylong_json::{JsonValue, Object, Number}; + /// + /// let mut object = Object::new(); + /// object.insert(String::from("test"), JsonValue::Number(Number::from(123))); + /// + /// assert_eq!(object.get("test"), Some(&JsonValue::Number(Number::from(123)))); + /// assert_eq!(object.get("no_such_key"), None); + /// ``` + pub fn get(&self, key: &str) -> Option<&JsonValue> { + self.get_cursor(key)?.current().map(|(_, v)| v) + } + + /// Gets a mutable reference to the element in Object with the specified key. + /// If there is an element with the same name, returns the one with the smallest subscript. + /// + /// # Examples + /// ``` + /// use ylong_json::{JsonValue, Object}; + /// + /// let mut object = Object::new(); + /// object.insert(String::from("null"), JsonValue::Null); + /// + /// assert_eq!(object.get_mut("null"), Some(&mut JsonValue::Null)); + /// assert_eq!(object.get_mut("no_such_key"), None); + /// ``` + pub fn get_mut(&mut self, key: &str) -> Option<&mut JsonValue> { + // Using get_cursor_mut causes a problem referencing temporary variables. + self.get_node_mut(key).map(|n| &mut n.get_element_mut().1) + } + + /// Gets a common reference to the node in Object with the specified key. + /// If there is an element with the same name, returns the one with the smallest subscript. + /// + /// After getting a common reference to a node, the node cannot be released. Otherwise, undefined behavior occurs. + /// + /// # Examples + /// ``` + /// use ylong_json::{JsonValue, Object}; + /// + /// let mut object = Object::new(); + /// assert_eq!(object.get_node("no_such_key").is_none(), true); + /// + /// object.insert(String::from("null"), JsonValue::Null); + /// assert_eq!(object.get_node("null").is_some(), true); + /// ``` + pub fn get_node(&self, key: &str) -> Option<&Node<(String, JsonValue)>> { + self.get_cursor(key)?.current_node() + } + + /// Gets a mutable reference to the node in Object with the specified key. + /// If there is an element with the same name, returns the one with the smallest subscript. + /// + /// After getting a mutable reference to a node, the node cannot be released. Otherwise, undefined behavior occurs. + /// + /// # Examples + /// ``` + /// use ylong_json::{JsonValue, Object}; + /// + /// let mut object = Object::new(); + /// assert_eq!(object.get_node_mut("no_such_key").is_none(), true); + /// + /// object.insert(String::from("null"), JsonValue::Null); + /// assert_eq!(object.get_node_mut("null").is_some(), true); + /// ``` + pub fn get_node_mut(&mut self, key: &str) -> Option<&mut Node<(String, JsonValue)>> { + self.get_cursor_mut(key)?.current_node() + } + + /// Gets the last node. + #[cfg(feature = "c_adapter")] + pub(crate) fn last_node_mut(&mut self) -> Option<&mut Node<(String, JsonValue)>> { + let mut cursor = self.inner.cursor_back_mut(); + let _ = cursor.index()?; + cursor.current_node() + } + + /// Needs using this method to avoid the life cycle check, which involves unsafe operations. + pub(crate) fn get_key_mut_maybe_insert(&mut self, key: &str) -> &mut JsonValue { + let mut cursor = self.inner.cursor_front(); + let mut ptr = null(); + while cursor.index().is_some() { + let current = cursor.current().unwrap(); + if current.0 == key { + ptr = cursor.current_node_ptr(); + break; + } + cursor.move_next(); + } + + if ptr.is_null() { + self.insert(String::from(key), JsonValue::Null); + &mut self.inner.back_mut().unwrap().1 + } else { + unsafe { + &mut (*(ptr as *mut Node<(String, JsonValue)>)) + .get_element_mut() + .1 + } + } + } + + /// Gets the common cursor of the node corresponding to the specified key. + fn get_cursor(&self, key: &str) -> Option> { + let mut cursor = self.inner.cursor_front(); + while cursor.index().is_some() { + let (k, _) = cursor.current().unwrap(); + if key == k { + return Some(cursor); + } + cursor.move_next(); + } + None + } + + /// Gets the mutable cursor of the node corresponding to the specified key. + fn get_cursor_mut(&mut self, key: &str) -> Option> { + let mut cursor = self.inner.cursor_front_mut(); + while cursor.index().is_some() { + let (k, _) = cursor.current().unwrap(); + if key == k { + return Some(cursor); + } + cursor.move_next(); + } + None + } +} + +impl Display for Object { + fn fmt(&self, f: &mut Formatter<'_>) -> core::fmt::Result { + write!(f, "{{")?; + for (n, (key, value)) in self.inner.iter().enumerate() { + if n != 0 { + write!(f, ",")?; + } + write!(f, "\"{key}\":{value}")?; + } + write!(f, "}}") + } +} + +impl Debug for Object { + fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result { + Display::fmt(self, f) + } +} + +#[cfg(test)] +mod ut_linked_list { + use crate::{JsonValue, Object}; + + /// UT test for `Object::contains_key`. + /// + /// # Title + /// ut_object_contains_key + /// + /// # Brief + /// 1. Creates a `Object`. + /// 2. Calls `Object::contains_key` on it. + /// 3. Checks if the test results are correct. + #[test] + fn ut_object_contains_key() { + let object = object!("key1" => "value1"); + assert!(object.contains_key("key1")); + assert!(!object.contains_key("key2")); + } + + /// UT test for `Object::iter_mut`. + /// + /// # Title + /// ut_object_iter_mut + /// + /// # Brief + /// 1. Creates a `Object`. + /// 2. Calls `Object::iter_mut` on it. + /// 3. Checks if the test results are correct. + #[test] + fn ut_object_iter_mut() { + let mut object = object!("key1" => "value1"); + let mut iter_mut = object.iter_mut(); + assert_eq!( + iter_mut.next(), + Some(&mut (String::from("key1"), JsonValue::new_string("value1"))) + ); + assert_eq!(iter_mut.next(), None); + } + + /// UT test for `Object::get_mut`. + /// + /// # Title + /// ut_object_get_mut + /// + /// # Brief + /// 1. Creates a `Object`. + /// 2. Calls `Object::get_mut` on it. + /// 3. Checks if the test results are correct. + #[test] + fn ut_object_get_mut() { + let mut object = object!("key1" => "value1"); + assert_eq!( + object.get_mut("key1"), + Some(&mut JsonValue::new_string("value1")) + ); + assert_eq!(object.get_mut("key2"), None); + } + + /// UT test for `Object::get_node`. + /// + /// # Title + /// ut_object_get_node + /// + /// # Brief + /// 1. Creates a `Object`. + /// 2. Calls `Object::get_node` on it. + /// 3. Checks if the test results are correct. + #[test] + fn ut_object_get_node() { + let object = object!("key1" => "value1"); + assert!(object.get_node("key1").is_some()); + assert!(object.get_node("key2").is_none()); + } + + /// UT test for `Object::get_node_mut`. + /// + /// # Title + /// ut_object_get_node_mut + /// + /// # Brief + /// 1. Creates a `Object`. + /// 2. Calls `Object::get_node_mut` on it. + /// 3. Checks if the test results are correct. + #[test] + fn ut_object_get_node_mut() { + let mut object = object!("key1" => "value1"); + assert!(object.get_node_mut("key1").is_some()); + assert!(object.get_node_mut("key2").is_none()); + } + + /// UT test for `Object::fmt`. + /// + /// # Title + /// ut_object_fmt + /// + /// # Brief + /// 1. Creates a `Object`. + /// 2. Calls `Object::fmt` on it. + /// 3. Checks if the test results are correct. + #[test] + fn ut_object_fmt() { + let object = object!("key1" => "value1"; "key2" => "value2"); + assert_eq!( + format!("{object}"), + "{\"key1\":\"value1\",\"key2\":\"value2\"}" + ); + assert_eq!( + format!("{object:?}"), + "{\"key1\":\"value1\",\"key2\":\"value2\"}" + ); + } +} diff --git a/src/value/object/vec.rs b/src/value/object/vec.rs new file mode 100644 index 0000000000000000000000000000000000000000..b4dd3280d1ba6d3866bc3cf7eb332c4ee4fa082a --- /dev/null +++ b/src/value/object/vec.rs @@ -0,0 +1,366 @@ +/* + * Copyright (c) 2023 Huawei Device Co., Ltd. + * Licensed 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. + */ + +use crate::JsonValue; +use core::fmt::{Debug, Display, Formatter}; +use core::slice::{Iter, IterMut}; + +/// Object type, implemented using Vec. +/// +/// # Situation +/// 1. When the average number of entries x under Object is about 15 <= x <= 100. +/// +/// 2. When the average number of Object entries x is about 101 <= x <= 1024, and the creation to +/// query ratio (the average number of queries created once) < 600. +/// +/// 3. When the average number of objects x is about 1025 <= x <= 10000, and the creation to +/// query ratio (the average number of queries created once) < 500. +/// +/// # Attention +/// * Only opening the 'vec_object' feature, this Object type can be used , and it conflicts with other Objects. +/// +/// * This Object ** does not provide the ** de-duplicate function. +/// * Users are required to ensure that there are no duplicate entries. +/// +/// * The output order of this Object is the same as the insertion order. +/// # Examples +/// ``` +/// use ylong_json::Object; +/// +/// let object = Object::new(); +/// assert_eq!(object.is_empty(), true); +/// ``` +#[derive(Default, Clone)] +pub struct Object { + inner: Vec<(String, JsonValue)>, +} + +impl Object { + /// Creates an empty Object. + /// + /// # Examples + /// ``` + /// use ylong_json::Object; + /// + /// let object = Object::new(); + /// assert_eq!(object.is_empty(), true); + /// ``` + pub fn new() -> Self { + Self { inner: Vec::new() } + } + + /// Gets the length of Object. + /// + /// # Examples + /// ``` + /// use ylong_json::{JsonValue, Object}; + /// + /// let mut object = Object::new(); + /// assert_eq!(object.len(), 0); + /// object.insert(String::from("null"), JsonValue::Null); + /// assert_eq!(object.len(), 1); + /// ``` + pub fn len(&self) -> usize { + self.inner.len() + } + + /// Determines whether the Object is empty. + /// + /// # Examples + /// ``` + /// use ylong_json::{JsonValue, Object}; + /// + /// let mut object = Object::new(); + /// assert_eq!(object.is_empty(), true); + /// object.insert(String::from("null"), JsonValue::Null); + /// assert_eq!(object.is_empty(), false); + /// ``` + pub fn is_empty(&self) -> bool { + self.inner.is_empty() + } + + /// Checks whether the specified key exists in the Object. + /// + /// # Examples + /// ``` + /// use ylong_json::{JsonValue, Object, Number}; + /// + /// let mut object = Object::new(); + /// object.insert(String::from("null"), JsonValue::Null); + /// + /// assert_eq!(object.contains_key("null"), true); + /// assert_eq!(object.contains_key("no_such_key"), false); + /// ``` + pub fn contains_key(&self, key: &str) -> bool { + self.inner.iter().any(|(k, _)| k == key) + } + + /// Inserts the specified key and value into an Object, appending them to the end without deduplication. + /// + /// # Examples + /// ``` + /// use ylong_json::{JsonValue, Object}; + /// + /// let mut object = Object::new(); + /// assert_eq!(object.len(), 0); + /// object.insert(String::from("null"), JsonValue::Null); + /// assert_eq!(object.len(), 1); + /// ``` + pub fn insert(&mut self, key: String, value: JsonValue) { + self.inner.push((key, value)) + } + + /// Removes the element under the specified key from the Object.If there is an element with + /// the same name in the Object, deletes the one with the smallest subscript. + /// + /// # Examples + /// ``` + /// use ylong_json::{JsonValue, Object, Number}; + /// + /// let mut object = Object::new(); + /// object.insert(String::from("null"), JsonValue::Null); + /// assert_eq!(object.len(), 1); + /// assert_eq!(object.remove("null"), Some(JsonValue::Null)); + /// assert_eq!(object.len(), 0); + /// ``` + pub fn remove(&mut self, key: &str) -> Option { + let pos = self.inner.iter().position(|(k, _)| k == key)?; + Some(self.inner.remove(pos).1) + } + + /// Gets a common iterator of Object. + /// + /// # Examples + /// ``` + /// use ylong_json::Object; + /// + /// let object = Object::new(); + /// let iter = object.iter(); + /// ``` + pub fn iter(&self) -> Iter<'_, (String, JsonValue)> { + self.inner.iter() + } + + /// Gets a mutable iterator of Object. + /// + /// # Examples + /// ``` + /// use ylong_json::Object; + /// + /// let mut object = Object::new(); + /// let iter_mut = object.iter_mut(); + /// ``` + pub fn iter_mut(&mut self) -> IterMut<'_, (String, JsonValue)> { + self.inner.iter_mut() + } + + /// Gets a common reference to the element in Object with the specified key. + /// If there is an element with the same name, returns the one with the smallest subscript. + /// + /// # Examples + /// ``` + /// use ylong_json::{JsonValue, Object, Number}; + /// + /// let mut object = Object::new(); + /// object.insert(String::from("test"), JsonValue::Number(Number::from(123))); + /// + /// assert_eq!(object.get("test"), Some(&JsonValue::Number(Number::from(123)))); + /// assert_eq!(object.get("no_such_key"), None); + /// ``` + pub fn get(&self, key: &str) -> Option<&JsonValue> { + self.inner.iter().find(|(k, _)| k == key).map(|(_, v)| v) + } + + /// Gets a mutable reference to the element in Object with the specified key. + /// If there is an element with the same name, returns the one with the smallest subscript. + /// + /// # Examples + /// ``` + /// use ylong_json::{JsonValue, Object}; + /// + /// let mut object = Object::new(); + /// object.insert(String::from("null"), JsonValue::Null); + /// + /// assert_eq!(object.get_mut("null"), Some(&mut JsonValue::Null)); + /// assert_eq!(object.get_mut("no_such_key"), None); + /// ``` + pub fn get_mut(&mut self, key: &str) -> Option<&mut JsonValue> { + self.inner + .iter_mut() + .find(|(k, _)| k == key) + .map(|(_, v)| v) + } + + /// Gets a mutable reference to the last element. + pub(crate) fn last_mut(&mut self) -> Option<&mut JsonValue> { + self.inner.last_mut().map(|(_, v)| v) + } + + pub(crate) fn get_mut_by_position(&mut self, index: usize) -> Option<&mut JsonValue> { + self.inner.get_mut(index).map(|(_, v)| v) + } +} + +impl PartialEq for Object { + /// Determines whether two objects are equal. + /// + /// The condition for two objects to be equal is that the two objects are of equal length + /// and the key-value pair can be one-to-one and exactly equal. + /// + /// # Examples + /// ``` + /// use ylong_json::{Object, JsonValue}; + /// + /// let object1 = Object::new(); + /// let object2 = Object::new(); + /// let mut object3 = Object::new(); + /// object3.insert("test".to_string(), JsonValue::Null); + /// + /// assert_eq!(object1, object2); + /// assert_ne!(object1, object3); + /// ``` + fn eq(&self, other: &Self) -> bool { + if self.len() != other.len() { + return false; + } + for (k, v) in self.iter() { + if other.get(k) != Some(v) { + return false; + } + } + true + } +} + +impl Display for Object { + fn fmt(&self, f: &mut Formatter<'_>) -> core::fmt::Result { + write!(f, "{{")?; + for (n, (key, value)) in self.inner.iter().enumerate() { + if n != 0 { + write!(f, ",")?; + } + write!(f, "\"{key}\":{value}")?; + } + write!(f, "}}") + } +} + +impl Debug for Object { + fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result { + Display::fmt(self, f) + } +} + +#[cfg(test)] +mod ut_vec { + use crate::{JsonValue, Object}; + + /// UT test for `Object::contains_key`. + /// + /// # Title + /// ut_object_contains_key + /// + /// # Brief + /// 1. Creates a `Object`. + /// 2. Calls `Object::contains_key` on it. + /// 3. Checks if the test results are correct. + #[test] + fn ut_object_contains_key() { + let object = object!("key1" => "value1"); + assert!(object.contains_key("key1")); + assert!(!object.contains_key("key2")); + } + + /// UT test for `Object::iter_mut`. + /// + /// # Title + /// ut_object_iter_mut + /// + /// # Brief + /// 1. Creates a `Object`. + /// 2. Calls `Object::iter_mut` on it. + /// 3. Checks if the test results are correct. + #[test] + fn ut_object_iter_mut() { + let mut object = object!("key1" => "value1"); + let mut iter_mut = object.iter_mut(); + assert_eq!( + iter_mut.next(), + Some(&mut (String::from("key1"), JsonValue::new_string("value1"))) + ); + assert_eq!(iter_mut.next(), None); + } + + /// UT test for `Object::get_mut`. + /// + /// # Title + /// ut_object_get_mut + /// + /// # Brief + /// 1. Creates a `Object`. + /// 2. Calls `Object::get_mut` on it. + /// 3. Checks if the test results are correct. + #[test] + fn ut_object_get_mut() { + let mut object = object!("key1" => "value1"); + assert_eq!( + object.get_mut("key1"), + Some(&mut JsonValue::new_string("value1")) + ); + assert_eq!(object.get_mut("key2"), None); + } + + /// UT test for `Object::fmt`. + /// + /// # Title + /// ut_object_fmt + /// + /// # Brief + /// 1. Creates a `Object`. + /// 2. Calls `Object::fmt` on it. + /// 3. Checks if the test results are correct. + #[test] + fn ut_object_fmt() { + let object = object!("key1" => "value1"; "key2" => "value2"); + assert_eq!( + format!("{object}"), + "{\"key1\":\"value1\",\"key2\":\"value2\"}" + ); + assert_eq!( + format!("{object:?}"), + "{\"key1\":\"value1\",\"key2\":\"value2\"}" + ); + } + + /// UT test for `Object::eq`. + /// + /// # Title + /// ut_object_fmt + /// + /// # Brief + /// 1. Creates a `Object`. + /// 2. Calls `Object::eq` on it. + /// 3. Checks if the test results are correct. + #[test] + fn ut_object_eq() { + let object1 = object!("key1" => "value1"); + let object2 = object!("key1" => "value1"; "key2" => "value2"); + let object3 = object!("key1" => "value1"; "key3" => "value3"); + + assert_eq!(object1, object1); + assert_ne!(object1, object2); + assert_ne!(object2, object3); + } +} diff --git a/tests/sdv_adapter_test.rs b/tests/sdv_adapter_test.rs new file mode 100644 index 0000000000000000000000000000000000000000..e8f84287390903b6b96b65c62e4df680e0e7ddef --- /dev/null +++ b/tests/sdv_adapter_test.rs @@ -0,0 +1,265 @@ +/* + * Copyright (c) 2023 Huawei Device Co., Ltd. + * Licensed 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. + */ + +use std::ffi::{CStr, CString}; +use std::os::raw::{c_char, c_double, c_int}; +use std::ptr::*; +use ylong_json::*; + +const RFC7159_EXAMPLE1: &str = r#" +{ + "Image": { + "Width": 800, + "Height": 600, + "Title": "View from 15th Floor", + "Thumbnail": { + "Url": "http://www.example.com/image/481989943", + "Height": 125, + "Width": 100 + }, + "Animated" : false, + "IDs": [116, 943, 234, 38793] + } +} +"#; + +macro_rules! test_json { + ($json: expr) => {{ + let target = str_to_c_char("Image"); + let image = ylong_json_get_object_item($json, target); + let _ = CString::from_raw(target); + + assert_eq!(ylong_json_is_object(image), 1); + + let target = str_to_c_char("Width"); + let width = ylong_json_get_object_item(image, target); + let _ = CString::from_raw(target); + let mut ptr: c_double = 0f64; + assert_eq!( + ylong_json_get_double_value_from_number(width, &mut ptr as *mut c_double), + 1 + ); + assert_eq!(ptr, 800f64); + + let target = str_to_c_char("Height"); + let height = ylong_json_get_object_item(image, target); + let _ = CString::from_raw(target); + let mut ptr: c_double = 0f64; + assert_eq!( + ylong_json_get_double_value_from_number(height, &mut ptr as *mut c_double), + 1 + ); + assert_eq!(ptr, 600f64); + + let target = str_to_c_char("Title"); + let title = ylong_json_get_object_item(image, target); + let _ = CString::from_raw(target); + let mut ptr: *mut c_char = null_mut::(); + assert_eq!( + ylong_json_get_value_from_string(title, &mut ptr as *mut *mut c_char), + 1 + ); + assert_eq!( + CStr::from_ptr(ptr).to_str().unwrap(), + "View from 15th Floor" + ); + + let target = str_to_c_char("Thumbnail"); + let thumbnail = ylong_json_get_object_item(image, target); + let _ = CString::from_raw(target); + assert_eq!(ylong_json_is_object(thumbnail), 1); + + let target = str_to_c_char("Url"); + let url = ylong_json_get_object_item(thumbnail, target); + let _ = CString::from_raw(target); + let mut ptr: *mut c_char = null_mut::(); + assert_eq!( + ylong_json_get_value_from_string(url, &mut ptr as *mut *mut c_char), + 1 + ); + assert_eq!( + CStr::from_ptr(ptr).to_str().unwrap(), + "http://www.example.com/image/481989943" + ); + + let target = str_to_c_char("Height"); + let height = ylong_json_get_object_item(thumbnail, target); + let _ = CString::from_raw(target); + let mut ptr: c_double = 0f64; + assert_eq!( + ylong_json_get_double_value_from_number(height, &mut ptr as *mut c_double), + 1 + ); + assert_eq!(ptr, 125f64); + + let target = str_to_c_char("Width"); + let width = ylong_json_get_object_item(thumbnail, target); + let _ = CString::from_raw(target); + let mut ptr: c_double = 0f64; + assert_eq!( + ylong_json_get_double_value_from_number(width, &mut ptr as *mut c_double), + 1 + ); + assert_eq!(ptr, 100f64); + + let target = str_to_c_char("Animated"); + let animated = ylong_json_get_object_item(image, target); + let _ = CString::from_raw(target); + let mut ptr: c_int = 0; + assert_eq!( + ylong_json_get_value_from_bool(animated, &mut ptr as *mut c_int), + 1 + ); + assert_eq!(ptr, 0); + + let target = str_to_c_char("IDs"); + let ids = ylong_json_get_object_item(image, target); + let _ = CString::from_raw(target); + + assert_eq!(ylong_json_is_array(ids), 1); + + let item = ylong_json_get_array_item(ids, 0); + let mut ptr = 0f64; + assert_eq!( + ylong_json_get_double_value_from_number(item, &mut ptr as *mut c_double), + 1 + ); + assert_eq!(ptr, 116f64); + + let item = ylong_json_get_array_item(ids, 1); + let mut ptr = 0f64; + assert_eq!( + ylong_json_get_double_value_from_number(item, &mut ptr as *mut c_double), + 1 + ); + assert_eq!(ptr, 943f64); + + let item = ylong_json_get_array_item(ids, 2); + let mut ptr = 0f64; + assert_eq!( + ylong_json_get_double_value_from_number(item, &mut ptr as *mut c_double), + 1 + ); + assert_eq!(ptr, 234f64); + + let item = ylong_json_get_array_item(ids, 3); + let mut ptr = 0f64; + assert_eq!( + ylong_json_get_double_value_from_number(item, &mut ptr as *mut c_double), + 1 + ); + assert_eq!(ptr, 38793f64); + }}; +} + +#[test] +fn sdv_adapter_test() { + unsafe { + sdv_adapter_parse_and_print(); + sdv_adapter_parse_memory_check(); + } +} + +unsafe fn str_to_c_char(str: &str) -> *mut c_char { + CString::from_vec_unchecked(str.as_bytes().to_vec()).into_raw() +} + +unsafe fn sdv_adapter_parse_and_print() { + let text = str_to_c_char(RFC7159_EXAMPLE1); + + let msg = null_mut::(); + let mut json = Some(ylong_json_parse( + text, + &msg as *const *mut c_char as *mut *mut c_char, + )); + + for _ in 0..1000 { + let curr = json.take().unwrap(); + + let curr_str = ylong_json_print_unformatted(curr); + + let msg = null_mut::(); + let new = ylong_json_parse(curr_str, &msg as *const *mut c_char as *mut *mut c_char); + + let _ = CString::from_raw(curr_str); + + test_json!(new); + + json = Some(new); + ylong_json_delete(curr); + } + + ylong_json_delete(json.take().unwrap()); + + // 析构 text + let _ = CString::from_raw(text); +} + +unsafe fn sdv_adapter_parse_memory_check() { + const TEXT: &str = r#" +{ + "null": null, + "true": true, + "false": false, + "number": 3.14, + "string": "Hello World!", + "array": [1, 2, 3], + "object": { + "key1": 1, + "key2": 2, + "key3": 3 + } +} +"#; + + let text = str_to_c_char(TEXT); + let msg = null_mut::(); + let json = ylong_json_parse(text, &msg as *const *mut c_char as *mut *mut c_char); + let _ = Box::from_raw(text); + + assert!(msg.is_null()); + let result = ylong_json_print_unformatted(json); + ylong_json_free_string(result); + + let duplicate = ylong_json_duplicate(json, 1); + ylong_json_delete(duplicate); + + let null = ylong_json_create_null(); + ylong_json_delete(null); + + let index = str_to_c_char("string"); + let string = ylong_json_get_object_item(json, index); + let content = null_mut::(); + ylong_json_get_value_from_string(string, &content as *const *mut c_char as *mut *mut c_char); + let _ = Box::from_raw(index); + + let null = ylong_json_create_null(); + let index = str_to_c_char("123"); + ylong_json_add_item_to_object(json, index, null); + + let content = str_to_c_char("aaaa"); + let string = ylong_json_create_string(content); + ylong_json_replace_object_item_by_index(json, index, string); + let _ = Box::from_raw(content); + + let removed = ylong_json_remove_object_item_by_index(json, index); + ylong_json_delete(removed); + let _ = Box::from_raw(index); + + extern "C" fn func(_value: *mut YlongJson) {} + ylong_json_for_each_object_item(json, func); + + ylong_json_delete(json); +} diff --git a/tests/ylong_json_sdv_test.rs b/tests/ylong_json_sdv_test.rs new file mode 100644 index 0000000000000000000000000000000000000000..85ac4759714b319400bb4ce96b70f4a4d956f6f3 --- /dev/null +++ b/tests/ylong_json_sdv_test.rs @@ -0,0 +1,381 @@ +/* + * Copyright (c) 2023 Huawei Device Co., Ltd. + * Licensed 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. + */ + +use ylong_json::{Array, JsonValue, Object}; + +const RFC7159_EXAMPLE1: &str = r#" +{ + "Image": { + "Width": 800, + "Height": 600, + "Title": "View from 15th Floor", + "Thumbnail": { + "Url": "http://www.example.com/image/481989943", + "Height": 125, + "Width": 100 + }, + "Animated" : false, + "IDs": [116, 943, 234, 38793] + } +} +"#; + +const RFC7159_EXAMPLE2: &str = r#" +[ + { + "precision": "zip", + "Latitude": 37.7668, + "Longitude": -122.3959, + "Address": "", + "City": "SAN FRANCISCO", + "State": "CA", + "Zip": "94107", + "Country": "US" + }, + { + "precision": "zip", + "Latitude": 37.371991, + "Longitude": -122.026020, + "Address": "", + "City": "SUNNYVALE", + "State": "CA", + "Zip": "94085", + "Country": "US" + } +] +"#; + +const JSON_PARSE_TEST: &str = r#" +[ + { + "null1": null + }, + { + "boolean1": true, + "boolean2": false + }, + { + "number1": 0, + "number2": -0, + "number3": 123, + "number4": -123, + "number5": 123.456, + "number6": -123.456, + "number7": 123.456e+7, + "number8": 123.456e-7, + "number9": 123.456E+7, + "number10": 123.456E-7, + "number11": -123.456e+7, + "number12": -123.456e-7, + "number13": -123.456E+7, + "number14": -123.456E-7, + "number15": 0.0, + "number16": -0.0e+7, + "number17": 3e2 + }, + { + "string1": "", + "string2": "Hello World", + "string3": "abcdefghijklmnopqrstuvwxyz", + "string4": "ABCDEFGHIJKLMNOPQRSTUVWXYZ", + "string5": "0123456789", + "string6": " \b\f\n\r\t", + "string7": "\"\\\/", + "string8": "`1~!@#$%^&*()_+-={':[,]}|;.?", + "string9": "\u0123\u4567\u89AB\uCDEF\uabcd\uef4A" + }, + { + "array1": [], + "array2": [ + ], + "array3": [null,true,0.0,"string",[],{}], + "array4": [ + null , true, 0.0 , + "string", [] + , {} ], + "array5": [[[[[[["nest"]]]]]]] + }, + { + "object1": {}, + "object2": { + }, + "object3": {"key1":null,"key2":true,"key3":0.0,"key4":"string","key5":[],"key6":{}}, + "object4": { + "key1" : null , "key2" + : true , "key3" : + 0.0 , "key4":"string" , + "key5": [], "key6": { + } + }, + "object5": {"nest1": {"nest2": {"nest3": {"nest4": {}}}}} + }, + { + "": "key1", + "\/\\\"\uCAFE\uBABE\uAB98\uFCDE\ubcda\uef4A\b\f\n\r\t`1~!@#$%^&*()_+-=[]{}|;:',./<>?" : "key2" + }, + { + "key_value1" + : "value" + , "key_value2" : [ + ] , "key_value3" : + {} + } +] +"#; + +macro_rules! rfc7159_example1_check { + ($json: expr) => { + assert_eq!($json["Image"]["Width"], 800.into()); + assert_eq!($json["Image"]["Height"], 600.into()); + assert_eq!($json["Image"]["Title"], "View from 15th Floor".into()); + assert_eq!( + $json["Image"]["Thumbnail"]["Url"], + "http://www.example.com/image/481989943".into() + ); + assert_eq!($json["Image"]["Thumbnail"]["Height"], 125.into()); + assert_eq!($json["Image"]["Thumbnail"]["Width"], 100.into()); + assert_eq!($json["Image"]["Animated"], false.into()); + assert_eq!($json["Image"]["IDs"][0], 116.into()); + assert_eq!($json["Image"]["IDs"][1], 943.into()); + assert_eq!($json["Image"]["IDs"][2], 234.into()); + assert_eq!($json["Image"]["IDs"][3], 38793.into()); + }; +} + +macro_rules! rfc7159_example2_check { + ($json: expr) => { + assert_eq!($json[0]["precision"], "zip".into()); + assert_eq!($json[0]["Latitude"], 37.7668.into()); + assert_eq!($json[0]["Longitude"], (-122.3959).into()); + assert_eq!($json[0]["Address"], "".into()); + assert_eq!($json[0]["City"], "SAN FRANCISCO".into()); + assert_eq!($json[0]["State"], "CA".into()); + assert_eq!($json[0]["Zip"], "94107".into()); + assert_eq!($json[0]["Country"], "US".into()); + assert_eq!($json[1]["precision"], "zip".into()); + assert_eq!($json[1]["Latitude"], 37.371991.into()); + assert_eq!($json[1]["Longitude"], (-122.026020).into()); + assert_eq!($json[1]["Address"], "".into()); + assert_eq!($json[1]["City"], "SUNNYVALE".into()); + assert_eq!($json[1]["State"], "CA".into()); + assert_eq!($json[1]["Zip"], "94085".into()); + assert_eq!($json[1]["Country"], "US".into()); + }; +} + +macro_rules! json_parse_test_check { + ($json: expr) => { + assert_eq!($json[0]["null1"], JsonValue::new_null()); + assert_eq!($json[1]["boolean1"], true.into()); + assert_eq!($json[1]["boolean2"], false.into()); + assert_eq!($json[2]["number1"], 0.into()); + assert_eq!($json[2]["number2"], 0.into()); + assert_eq!($json[2]["number3"], 123.into()); + assert_eq!($json[2]["number4"], (-123).into()); + assert_eq!($json[2]["number5"], 123.456.into()); + assert_eq!($json[2]["number6"], (-123.456).into()); + assert_eq!($json[2]["number7"], 1234560000.into()); + assert_eq!($json[2]["number8"], 0.0000123456.into()); + assert_eq!($json[2]["number9"], 1234560000.into()); + assert_eq!($json[2]["number10"], 0.0000123456.into()); + assert_eq!($json[2]["number11"], (-1234560000).into()); + assert_eq!($json[2]["number12"], (-0.0000123456).into()); + assert_eq!($json[2]["number13"], (-1234560000).into()); + assert_eq!($json[2]["number14"], (-0.0000123456).into()); + assert_eq!($json[2]["number15"], 0.into()); + assert_eq!($json[2]["number16"], 0.into()); + assert_eq!($json[2]["number17"], 300.into()); + assert_eq!($json[3]["string1"], "".into()); + assert_eq!($json[3]["string2"], "Hello World".into()); + assert_eq!($json[3]["string3"], "abcdefghijklmnopqrstuvwxyz".into()); + assert_eq!($json[3]["string4"], "ABCDEFGHIJKLMNOPQRSTUVWXYZ".into()); + assert_eq!($json[3]["string5"], "0123456789".into()); + assert_eq!($json[3]["string6"], " \u{0008}\u{000c}\n\r\t".into()); + assert_eq!($json[3]["string7"], "\"\\/".into()); + assert_eq!($json[3]["string8"], "`1~!@#$%^&*()_+-={':[,]}|;.?".into()); + assert_eq!($json[3]["string9"], "\u{0123}\u{4567}\u{89AB}\u{CDEF}\u{abcd}\u{ef4A}".into()); + assert_eq!($json[4]["array1"], Array::new().into()); + assert_eq!($json[4]["array2"], Array::new().into()); + assert_eq!($json[4]["array3"][0], JsonValue::new_null()); + assert_eq!($json[4]["array3"][1], true.into()); + assert_eq!($json[4]["array3"][2], 0.into()); + assert_eq!($json[4]["array3"][3], "string".into()); + assert_eq!($json[4]["array3"][4], Array::new().into()); + assert_eq!($json[4]["array3"][5], Object::new().into()); + assert_eq!($json[4]["array4"][0], JsonValue::new_null()); + assert_eq!($json[4]["array4"][1], true.into()); + assert_eq!($json[4]["array4"][2], 0.into()); + assert_eq!($json[4]["array4"][3], "string".into()); + assert_eq!($json[4]["array4"][4], Array::new().into()); + assert_eq!($json[4]["array4"][5], Object::new().into()); + assert_eq!($json[4]["array5"][0][0][0][0][0][0][0], "nest".into()); + assert_eq!($json[5]["object1"], Object::new().into()); + assert_eq!($json[5]["object2"], Object::new().into()); + assert_eq!($json[5]["object3"]["key1"], JsonValue::new_null()); + assert_eq!($json[5]["object3"]["key2"], true.into()); + assert_eq!($json[5]["object3"]["key3"], 0.into()); + assert_eq!($json[5]["object3"]["key4"], "string".into()); + assert_eq!($json[5]["object3"]["key5"], Array::new().into()); + assert_eq!($json[5]["object3"]["key6"], Object::new().into()); + assert_eq!($json[5]["object4"]["key1"], JsonValue::new_null()); + assert_eq!($json[5]["object4"]["key2"], true.into()); + assert_eq!($json[5]["object4"]["key3"], 0.into()); + assert_eq!($json[5]["object4"]["key4"], "string".into()); + assert_eq!($json[5]["object4"]["key5"], Array::new().into()); + assert_eq!($json[5]["object4"]["key6"], Object::new().into()); + assert_eq!($json[5]["object5"]["nest1"]["nest2"]["nest3"]["nest4"], Object::new().into()); + assert_eq!($json[6][""], "key1".into()); + assert_eq!( + $json[6]["/\\\"\u{CAFE}\u{BABE}\u{AB98}\u{FCDE}\u{bcda}\u{ef4A}\u{0008}\u{000c}\n\r\t`1~!@#$%^&*()_+-=[]{}|;:',./<>?"], + "key2".into() + ); + assert_eq!($json[7]["key_value1"], "value".into()); + assert_eq!($json[7]["key_value2"], Array::new().into()); + assert_eq!($json[7]["key_value3"], Object::new().into()); + } +} + +/* + * @title ylong_json sdv 测试用例 + * @design 使用路径覆盖 + * @precon 无 + * @brief 1. 准备一个 json 文本 + * 2. 根据该文本创建一个 Json 实例 + * 3. 修改该实例中的值 + * 4. 以字符串形式输出到指定位置 + * 5. 校验输出结果 + * @expect 1. 得到预期输出的字符串。 + * @auto 是 + */ +#[test] +fn sdv_ylong_json() { + sdv_json_parse(); + sdv_json_modify(); + sdv_json_output(); +} + +fn sdv_json_parse() { + // 测试 RFC7159 13. Examples 里的两个 json 文本。 + let json = JsonValue::from_text(RFC7159_EXAMPLE1).unwrap(); + rfc7159_example1_check!(json); + + let json = JsonValue::from_text(RFC7159_EXAMPLE2).unwrap(); + rfc7159_example2_check!(json); + + let json = JsonValue::from_text(JSON_PARSE_TEST).unwrap(); + json_parse_test_check!(json); +} + +fn sdv_json_modify() { + let json_text = "{}"; + let mut json = JsonValue::from_text(json_text).unwrap(); + // 初始时 json 为空。 + assert!(json.try_as_object().unwrap().is_empty()); + + json["null"] = JsonValue::new_null(); + json["boolean"] = true.into(); + json["number"] = 123.into(); + json["string"] = "Hello World".into(); + json["array"] = Array::new().into(); + json["object"] = Object::new().into(); + + assert!(json["null"].is_null()); + assert_eq!(json["boolean"], true.into()); + assert_eq!(json["number"], 123.into()); + assert_eq!(json["string"], "Hello World".into()); + assert_eq!(json["array"], Array::new().into()); + assert_eq!(json["object"], Object::new().into()); + assert_eq!(json.try_as_object().unwrap().len(), 6); + + json["array"][0] = 123.into(); + json["array"][1] = "string".into(); + json["array"][2] = JsonValue::new_null(); + + assert_eq!(json["array"][0], 123.into()); + assert_eq!(json["array"][1], "string".into()); + assert_eq!(json["array"][2], JsonValue::new_null()); + assert_eq!(json["array"].try_as_array().unwrap().len(), 3); + + json["array"] = Array::new().into(); + assert_eq!(json["array"].try_as_array().unwrap().len(), 0); + + json["object"]["number"] = 123.into(); + json["object"]["string"] = "string".into(); + json["object"]["null"] = JsonValue::new_null(); + + assert_eq!(json["object"]["number"], 123.into()); + assert_eq!(json["object"]["string"], "string".into()); + assert_eq!(json["object"]["null"], JsonValue::new_null()); + assert_eq!(json["object"].try_as_object().unwrap().len(), 3); + + json["object"] = Object::new().into(); + assert_eq!(json["object"].try_as_object().unwrap().len(), 0); +} + +#[allow(unused_assignments)] +fn sdv_json_output() { + const LOOPS_NUM: usize = 1000; + + let mut json = JsonValue::from_text(RFC7159_EXAMPLE1).unwrap(); + let mut vec = Vec::new(); + + for _ in 0..LOOPS_NUM { + // 将 json 内容写入 vec 中 + vec.clear(); + assert!(json.formatted_encode(&mut vec).is_ok()); + + // 通过 vec 重新生成一个 json 实例 + let temp = JsonValue::from_text(&vec).unwrap(); + + // 比较内容是否发生变化 + rfc7159_example1_check!(temp); + + json = temp; + } + + let mut json = JsonValue::from_text(RFC7159_EXAMPLE2).unwrap(); + let mut vec = Vec::new(); + + for _ in 0..LOOPS_NUM { + // 将 json 内容写入 vec 中 + vec.clear(); + assert!(json.formatted_encode(&mut vec).is_ok()); + + // 通过 vec 重新生成一个 json 实例 + let temp = JsonValue::from_text(&vec).unwrap(); + + // 比较内容是否发生变化 + rfc7159_example2_check!(temp); + + json = temp; + } + + let mut json = JsonValue::from_text(JSON_PARSE_TEST).unwrap(); + let mut vec = Vec::new(); + + for _ in 0..LOOPS_NUM { + // 将 json 内容写入 vec 中 + vec.clear(); + assert!(json.formatted_encode(&mut vec).is_ok()); + + // 通过 vec 重新生成一个 json 实例 + let temp = JsonValue::from_text(&vec).unwrap(); + + // 比较内容是否发生变化 + json_parse_test_check!(temp); + + json = temp; + } +}