mirror of
https://github.com/openharmony/commonlibrary_rust_ylong_json.git
synced 2026-06-30 22:08:36 -04:00
+42
@@ -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 = []
|
||||
@@ -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.
|
||||
@@ -0,0 +1,71 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<!-- Copyright (c) 2021 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.
|
||||
|
||||
Notes:
|
||||
This is project config file for OpenHarmony OSS Audit Tool, if you have any questions or concerns, please email chenyaxun@huawei.com.
|
||||
-->
|
||||
<!-- OAT(OSS Audit Tool) configuration guide:
|
||||
basedir: Root dir, the basedir + project path is the real source file location.
|
||||
licensefile:
|
||||
1.If the project don't have "LICENSE" in root dir, please define all the license files in this project in , OAT will check license files according to this rule.
|
||||
|
||||
tasklist(only for batch mode):
|
||||
1. task: Define oat check thread, each task will start a new thread.
|
||||
2. task name: Only an name, no practical effect.
|
||||
3. task policy: Default policy for projects under this task, this field is required and the specified policy must defined in policylist.
|
||||
4. task filter: Default filefilter for projects under this task, this field is required and the specified filefilter must defined in filefilterlist.
|
||||
5. task project: Projects to be checked, the path field define the source root dir of the project.
|
||||
|
||||
|
||||
policyList:
|
||||
1. policy: All policyitems will be merged to default OAT.xml rules, the name of policy doesn't affect OAT check process.
|
||||
2. policyitem: The fields type, name, path, desc is required, and the fields rule, group, filefilter is optional,the default value is:
|
||||
<policyitem type="" name="" path="" desc="" rule="may" group="defaultGroup" filefilter="defaultPolicyFilter"/>
|
||||
3. policyitem type:
|
||||
"compatibility" is used to check license compatibility in the specified path;
|
||||
"license" is used to check source license header in the specified path;
|
||||
"copyright" is used to check source copyright header in the specified path;
|
||||
"import" is used to check source dependency in the specified path, such as import ... ,include ...
|
||||
"filetype" is used to check file type in the specified path, supported file types: archive, binary
|
||||
"filename" is used to check whether the specified file exists in the specified path(support projectroot in default OAT.xml), supported file names: LICENSE, README, README.OpenSource
|
||||
|
||||
4. policyitem name: This field is used for define the license, copyright, "*" means match all, the "!" prefix means could not match this value. For example, "!GPL" means can not use GPL license.
|
||||
5. policyitem path: This field is used for define the source file scope to apply this policyitem, the "!" prefix means exclude the files. For example, "!.*/lib/.*" means files in lib dir will be exclude while process this policyitem.
|
||||
6. policyitem rule and group: These two fields are used together to merge policy results. "may" policyitems in the same group means any one in this group passed, the result will be passed.
|
||||
7. policyitem filefilter: Used to bind filefilter which define filter rules.
|
||||
8. filefilter: Filter rules, the type filename is used to filter file name, the type filepath is used to filter file path.
|
||||
|
||||
Note:If the text contains special characters, please escape them according to the following rules:
|
||||
" == >
|
||||
& == >
|
||||
' == >
|
||||
< == >
|
||||
> == >
|
||||
-->
|
||||
<configuration>
|
||||
<oatconfig>
|
||||
<filefilterlist>
|
||||
<filefilter name="binaryFileTypePolicyFilter" desc="Filters for binary file policies" >
|
||||
<filteritem type="filename" name=".*.pem" desc="Testing of API for HTTPS configuration certificates requires a certificate input"/>
|
||||
</filefilter>
|
||||
<filefilter name="defaultPolicyFilter" desc="Filters for compatibility,license header policies">
|
||||
<filteritem type="filename" name=".*.toml" desc="toml treats as readme file"/>
|
||||
</filefilter>
|
||||
<filefilter name="copyrightPolicyFilter" desc="Filters for copyright policies">
|
||||
<filteritem type="filename" name=".*.toml" desc="toml treats as readme file"/>
|
||||
</filefilter>
|
||||
</filefilterlist>
|
||||
</oatconfig>
|
||||
</configuration>
|
||||
@@ -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/)
|
||||
@@ -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<dyn Read> = 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<String>,
|
||||
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::<Example>(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::<Example>(slice).unwrap();
|
||||
|
||||
|
||||
// 可以使用 from_reader 接口,从实现了 io::Write 的位置、文件、io流等生成实例。
|
||||
// 如果传入的文本内容不符合 JSON 语法,会返回对应的 Error。
|
||||
let mut file: File = File::open("./example.txt").unwrap();
|
||||
let example = from_reader::<Example>(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 # 测试目录
|
||||
```
|
||||
+328
@@ -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<dyn Read> = 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<String>,
|
||||
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::<Example>(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::<Example>(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::<Example>(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<V: Serialize> 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
|
||||
```
|
||||
@@ -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();
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
@@ -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}");
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
@@ -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"
|
||||
}
|
||||
}
|
||||
"#;
|
||||
@@ -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();
|
||||
}
|
||||
@@ -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
|
||||
);
|
||||
}
|
||||
+3409
File diff suppressed because it is too large
Load Diff
+214
@@ -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"));
|
||||
}
|
||||
}
|
||||
+1099
File diff suppressed because it is too large
Load Diff
+511
@@ -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<usize> {
|
||||
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}}",
|
||||
);
|
||||
}
|
||||
}
|
||||
+416
@@ -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<dyn std::error::Error>),
|
||||
|
||||
/// 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: Into<Box<dyn std::error::Error>>>(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<ParseError> for Error {
|
||||
fn from(e: ParseError) -> Self {
|
||||
Error::Parsing(e)
|
||||
}
|
||||
}
|
||||
|
||||
impl From<core::str::Utf8Error> for Error {
|
||||
fn from(_: core::str::Utf8Error) -> Self {
|
||||
Error::Utf8Transform
|
||||
}
|
||||
}
|
||||
|
||||
impl From<std::io::Error> 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<T>(msg: T) -> Self
|
||||
where
|
||||
T: Display,
|
||||
{
|
||||
Error::Custom(msg.to_string())
|
||||
}
|
||||
}
|
||||
|
||||
impl serde::de::Error for Error {
|
||||
fn custom<T>(msg: T) -> Self
|
||||
where
|
||||
T: Display,
|
||||
{
|
||||
Error::Custom(msg.to_string())
|
||||
}
|
||||
}
|
||||
|
||||
impl From<FromUtf8Error> for Error {
|
||||
fn from(_: FromUtf8Error) -> Self {
|
||||
Error::Utf8Transform
|
||||
}
|
||||
}
|
||||
|
||||
impl From<IntoStringError> 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",
|
||||
);
|
||||
}
|
||||
}
|
||||
+86
@@ -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};
|
||||
@@ -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<T> {
|
||||
head: *const Node<T>,
|
||||
tail: *const Node<T>,
|
||||
len: usize,
|
||||
marker: PhantomData<Box<Node<T>>>,
|
||||
}
|
||||
|
||||
impl<T> LinkedList<T> {
|
||||
/// 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<T>;
|
||||
|
||||
if self.tail.is_null() {
|
||||
self.head = node;
|
||||
} else {
|
||||
(*(self.tail as *mut Node<T>)).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<T> {
|
||||
if self.tail.is_null() {
|
||||
None
|
||||
} else {
|
||||
unsafe {
|
||||
let node = Box::from_raw(self.tail as *mut Node<T>);
|
||||
self.tail = node.prev;
|
||||
|
||||
if self.tail.is_null() {
|
||||
self.head = null();
|
||||
} else {
|
||||
(*(self.tail as *mut Node<T>)).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<T>)).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<T>> {
|
||||
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<T>> {
|
||||
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<T>);
|
||||
node.parent = self as *const LinkedList<T>;
|
||||
Some(node)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Removes a node from the linked list.
|
||||
pub(crate) unsafe fn unlink_node(&mut self, node: *const Node<T>) {
|
||||
let node = &mut (*(node as *mut Node<T>));
|
||||
|
||||
if node.prev.is_null() {
|
||||
self.head = node.next;
|
||||
} else {
|
||||
(*(node.prev as *mut Node<T>)).next = node.next;
|
||||
}
|
||||
|
||||
if node.next.is_null() {
|
||||
self.tail = node.prev;
|
||||
} else {
|
||||
(*(node.next as *mut Node<T>)).prev = node.prev;
|
||||
}
|
||||
|
||||
self.len -= 1;
|
||||
}
|
||||
}
|
||||
|
||||
impl<T> Default for LinkedList<T> {
|
||||
fn default() -> Self {
|
||||
Self::new()
|
||||
}
|
||||
}
|
||||
|
||||
impl<T: Debug> Debug for LinkedList<T> {
|
||||
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<T: PartialEq> PartialEq for LinkedList<T> {
|
||||
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<T: Clone> Clone for LinkedList<T> {
|
||||
fn clone(&self) -> Self {
|
||||
let mut new_list = LinkedList::new();
|
||||
for item in self.iter() {
|
||||
new_list.push_back(item.clone());
|
||||
}
|
||||
new_list
|
||||
}
|
||||
}
|
||||
|
||||
impl<T> Drop for LinkedList<T> {
|
||||
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<T: Send> Send for LinkedList<T> {}
|
||||
unsafe impl<T: Sync> Sync for LinkedList<T> {}
|
||||
|
||||
/// Linked list node, only through a linked list cursor to get the node.
|
||||
pub struct Node<T> {
|
||||
next: *const Node<T>,
|
||||
prev: *const Node<T>,
|
||||
parent: *const LinkedList<T>,
|
||||
element: T,
|
||||
}
|
||||
|
||||
impl<T> Node<T> {
|
||||
/// 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<T> {
|
||||
let list = unsafe { &mut *(self.parent as *mut LinkedList<T>) };
|
||||
let mut cursor = CursorMut {
|
||||
index: 0,
|
||||
current: self as *const Node<T>,
|
||||
list,
|
||||
};
|
||||
cursor.remove_current()
|
||||
}
|
||||
}
|
||||
|
||||
/// A common iterator of a linked list.
|
||||
pub struct Iter<'a, T: 'a> {
|
||||
head: *const Node<T>,
|
||||
tail: *const Node<T>,
|
||||
len: usize,
|
||||
marker: PhantomData<&'a Node<T>>,
|
||||
}
|
||||
|
||||
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<T>) };
|
||||
self.len -= 1;
|
||||
self.head = node.next;
|
||||
Some(&node.element)
|
||||
}
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn size_hint(&self) -> (usize, Option<usize>) {
|
||||
// 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<T>) };
|
||||
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<T>,
|
||||
tail: *const Node<T>,
|
||||
len: usize,
|
||||
marker: PhantomData<&'a mut Node<T>>,
|
||||
}
|
||||
|
||||
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<T>) };
|
||||
self.len -= 1;
|
||||
self.head = node.next;
|
||||
Some(&mut node.element)
|
||||
}
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn size_hint(&self) -> (usize, Option<usize>) {
|
||||
// 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<T>) };
|
||||
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<T>,
|
||||
list: &'a LinkedList<T>,
|
||||
}
|
||||
|
||||
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<usize> {
|
||||
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<T>> {
|
||||
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<T> {
|
||||
self.current
|
||||
}
|
||||
}
|
||||
|
||||
pub(crate) struct CursorMut<'a, T: 'a> {
|
||||
index: usize,
|
||||
current: *const Node<T>,
|
||||
list: &'a mut LinkedList<T>,
|
||||
}
|
||||
|
||||
impl<'a, T> CursorMut<'a, T> {
|
||||
/// Gets the index.
|
||||
#[inline]
|
||||
pub(crate) fn index(&self) -> Option<usize> {
|
||||
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<T>)).element) }
|
||||
}
|
||||
}
|
||||
|
||||
/// Gets a mutable reference to the current node.
|
||||
#[inline]
|
||||
pub(crate) fn current_node(&mut self) -> Option<&'a mut Node<T>> {
|
||||
if self.current.is_null() {
|
||||
None
|
||||
} else {
|
||||
unsafe {
|
||||
let node = &mut *(self.current as *mut Node<T>);
|
||||
node.parent = self.list as *mut LinkedList<T>;
|
||||
Some(node)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Deletes the node to which the cursor is pointing.
|
||||
#[inline]
|
||||
pub(crate) fn remove_current(&mut self) -> Option<T> {
|
||||
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<T>);
|
||||
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::<i32>::default(), LinkedList::<i32>::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);
|
||||
}
|
||||
}
|
||||
@@ -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<R: Read> {
|
||||
io: R,
|
||||
buf: Vec<u8>, // 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<Cache>,
|
||||
}
|
||||
|
||||
// A simple cache implementation for `IoReader`.
|
||||
struct Cache {
|
||||
cache: Vec<u8>,
|
||||
pre: usize, // Last cached location.
|
||||
}
|
||||
|
||||
impl Cache {
|
||||
/// Create a new `Cache`.
|
||||
fn new() -> Self {
|
||||
Self {
|
||||
cache: Vec::new(),
|
||||
pre: 0,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<R: Read> IoReader<R> {
|
||||
/// 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<bool> {
|
||||
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<bool> {
|
||||
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<R: Read> BytesReader for IoReader<R> {
|
||||
type Error = Error;
|
||||
|
||||
fn next(&mut self) -> Result<Option<u8>> {
|
||||
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<Option<u8>> {
|
||||
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<R: Read> Cacheable for IoReader<R> {
|
||||
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<usize> {
|
||||
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<Vec<u8>> {
|
||||
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<Vec<u8>> {
|
||||
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<u8>,
|
||||
idx: usize,
|
||||
}
|
||||
|
||||
impl TestIo {
|
||||
fn new(vec: Vec<u8>) -> Self {
|
||||
Self { vec, idx: 0 }
|
||||
}
|
||||
}
|
||||
|
||||
impl Read for TestIo {
|
||||
fn read(&mut self, buf: &mut [u8]) -> std::io::Result<usize> {
|
||||
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<usize> {
|
||||
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<usize> {
|
||||
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());
|
||||
}
|
||||
}
|
||||
@@ -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<Box<dyn std::error::Error>>;
|
||||
|
||||
/// Get the next character and move the cursor to the next place.
|
||||
fn next(&mut self) -> Result<Option<u8>, 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<Option<u8>, 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<usize>;
|
||||
|
||||
/// 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<Vec<u8>>;
|
||||
|
||||
/// 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<Vec<u8>>;
|
||||
}
|
||||
|
||||
/// `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<u8>` of the remainder.
|
||||
fn remainder_data(&self) -> Vec<u8>;
|
||||
}
|
||||
|
||||
/// `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<Option<&[u8]>, 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<Option<&[u8]>, 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);
|
||||
}
|
||||
}
|
||||
@@ -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<Cache>, // 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<Option<u8>, 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<Option<u8>, 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<usize> {
|
||||
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<Vec<u8>> {
|
||||
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<Vec<u8>> {
|
||||
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<u8> {
|
||||
self.remainder_slice().to_vec()
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a> NBytesReadable for SliceReader<'a> {
|
||||
fn next_n(&mut self, n: usize) -> Result<Option<&[u8]>, 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<Option<&[u8]>, 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'));
|
||||
}
|
||||
}
|
||||
@@ -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<W>
|
||||
where
|
||||
W: std::io::Write,
|
||||
{
|
||||
writer: W,
|
||||
element_num: Vec<usize>, // 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<u8>,
|
||||
}
|
||||
|
||||
impl std::io::Write for AuxiliaryWriter {
|
||||
fn write(&mut self, buf: &[u8]) -> std::io::Result<usize> {
|
||||
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<T>(value: &T) -> Result<String, Error>
|
||||
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<T, W>(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<W> {
|
||||
// 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<T>(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<T>(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<T>(
|
||||
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<usize>) -> Result<Self::SerializeSeq, Error> {
|
||||
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::SerializeTuple, Error> {
|
||||
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::SerializeTupleStruct, Error> {
|
||||
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::SerializeTupleVariant, Error> {
|
||||
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<usize>) -> Result<Self::SerializeMap, Error> {
|
||||
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::SerializeStruct, Error> {
|
||||
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::SerializeStructVariant, Error> {
|
||||
self.element_num.push(0);
|
||||
self.writer.write_all(b"{")?;
|
||||
variant.serialize(&mut *self)?;
|
||||
self.writer.write_all(b":{")?;
|
||||
Ok(self)
|
||||
}
|
||||
}
|
||||
|
||||
impl<W> Serializer<W>
|
||||
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<W> {
|
||||
// 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<T>(&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<W> {
|
||||
type Ok = ();
|
||||
type Error = Error;
|
||||
|
||||
fn serialize_element<T>(&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<W> {
|
||||
type Ok = ();
|
||||
type Error = Error;
|
||||
|
||||
fn serialize_field<T>(&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<W> {
|
||||
type Ok = ();
|
||||
type Error = Error;
|
||||
|
||||
fn serialize_field<T>(&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<W> {
|
||||
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<T>(&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<T>(&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<W> {
|
||||
type Ok = ();
|
||||
type Error = Error;
|
||||
|
||||
fn serialize_field<T>(&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<W> {
|
||||
type Ok = ();
|
||||
type Error = Error;
|
||||
|
||||
fn serialize_field<T>(&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<u32> = None;
|
||||
let expected = "null";
|
||||
assert_eq!(to_string(&value).unwrap(), expected);
|
||||
|
||||
let value: Option<u32> = 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());
|
||||
}
|
||||
}
|
||||
+1117
File diff suppressed because it is too large
Load Diff
+1769
File diff suppressed because it is too large
Load Diff
@@ -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;
|
||||
@@ -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<JsonValue>,
|
||||
}
|
||||
|
||||
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<JsonValue> {
|
||||
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<JsonValue> {
|
||||
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<JsonValue>> {
|
||||
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<JsonValue>> {
|
||||
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<JsonValue>> {
|
||||
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<JsonValue>> {
|
||||
self.inner.back_node_mut()
|
||||
}
|
||||
|
||||
/// Gets the common cursor of the specified index node.
|
||||
fn get_cursor(&self, index: usize) -> Option<Cursor<'_, JsonValue>> {
|
||||
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<CursorMut<'_, JsonValue>> {
|
||||
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]");
|
||||
}
|
||||
}
|
||||
@@ -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<JsonValue>,
|
||||
}
|
||||
|
||||
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<JsonValue> {
|
||||
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<JsonValue> {
|
||||
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]");
|
||||
}
|
||||
}
|
||||
@@ -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<JsonValue>;
|
||||
}
|
||||
|
||||
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<JsonValue> {
|
||||
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<JsonValue> {
|
||||
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<JsonValue> {
|
||||
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<JsonValue> {
|
||||
(**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());
|
||||
}
|
||||
}
|
||||
@@ -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<u64, Error> {
|
||||
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<i64, Error> {
|
||||
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<f64, Error> {
|
||||
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());
|
||||
}
|
||||
}
|
||||
@@ -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;
|
||||
@@ -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<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: 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<JsonValue> {
|
||||
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);
|
||||
}
|
||||
}
|
||||
@@ -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<JsonValue> {
|
||||
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<Cursor<'_, (String, JsonValue)>> {
|
||||
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<CursorMut<'_, (String, JsonValue)>> {
|
||||
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\"}"
|
||||
);
|
||||
}
|
||||
}
|
||||
@@ -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<JsonValue> {
|
||||
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);
|
||||
}
|
||||
}
|
||||
@@ -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::<c_char>();
|
||||
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::<c_char>();
|
||||
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::<c_char>();
|
||||
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::<c_char>();
|
||||
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::<c_char>();
|
||||
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::<c_char>();
|
||||
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);
|
||||
}
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user