Remove the `ffi-support` crate sources from this repo.
As of https://github.com/mozilla/application-services/issues/3463 the `ffi-support` crate lives in its own repo, and it's confusing having an old copy of it still hanging around in this one.
This commit is contained in:
Родитель
c560641cbb
Коммит
5fc2d7841c
|
@ -2,12 +2,6 @@ Unless otherwise specified (below and/or in individual files), the code in
|
|||
this repository is subject to the Mozilla Public License, version 2.0
|
||||
<LICENSE-MPL> or <https://www.mozilla.org/en-US/MPL/2.0/>.
|
||||
|
||||
- The ffi-support crate, in components/support/ffi, is dual licensed under the
|
||||
Apache License, Version 2.0 <components/support/ffi/LICENSE-APACHE> or
|
||||
<http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
|
||||
<components/support/ffi/LICENSE-MIT> or <http://opensource.org/licenses/MIT>,
|
||||
at your option.
|
||||
|
||||
- Some code in the rc_crypto crate (`components/support/rc_crypto`) is derived from the
|
||||
ring crate which is under the ISC license, reproduced below:
|
||||
|
||||
|
|
|
@ -966,34 +966,14 @@ checksum = "7360491ce676a36bf9bb3c56c1aa791658183a54d2744120f27285738d90465a"
|
|||
|
||||
[[package]]
|
||||
name = "ffi-support"
|
||||
version = "0.4.2"
|
||||
dependencies = [
|
||||
"backtrace",
|
||||
"lazy_static",
|
||||
"log 0.4.14",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "ffi-support"
|
||||
version = "0.4.2"
|
||||
version = "0.4.3"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "f85d4d1be103c0b2d86968f0b0690dc09ac0ba205b90adb0389b552869e5000e"
|
||||
checksum = "6f476e3c4fa6d9a3e0eeabd4f7555a382025ec05194675d72fa1db347e53ae6c"
|
||||
dependencies = [
|
||||
"lazy_static",
|
||||
"log 0.4.14",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "ffi-support-extra-test"
|
||||
version = "0.1.0"
|
||||
dependencies = [
|
||||
"env_logger",
|
||||
"ffi-support 0.4.2",
|
||||
"log 0.4.14",
|
||||
"rand 0.7.3",
|
||||
"rayon",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "find-places-db"
|
||||
version = "0.2.0"
|
||||
|
@ -1138,7 +1118,7 @@ dependencies = [
|
|||
"anyhow",
|
||||
"base64 0.12.3",
|
||||
"error-support",
|
||||
"ffi-support 0.4.2 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"ffi-support",
|
||||
"hex",
|
||||
"jwcrypto",
|
||||
"lazy_static",
|
||||
|
@ -1206,7 +1186,7 @@ version = "38.0.0"
|
|||
dependencies = [
|
||||
"bincode",
|
||||
"chrono",
|
||||
"ffi-support 0.4.2 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"ffi-support",
|
||||
"flate2",
|
||||
"log 0.4.14",
|
||||
"once_cell",
|
||||
|
@ -1224,7 +1204,7 @@ version = "38.0.0"
|
|||
dependencies = [
|
||||
"android_logger",
|
||||
"env_logger",
|
||||
"ffi-support 0.4.2 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"ffi-support",
|
||||
"glean-core",
|
||||
"libc",
|
||||
"log 0.4.14",
|
||||
|
@ -1628,7 +1608,7 @@ dependencies = [
|
|||
"anyhow",
|
||||
"env_logger",
|
||||
"error-support",
|
||||
"ffi-support 0.4.2 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"ffi-support",
|
||||
"interrupt-support",
|
||||
"lazy_static",
|
||||
"log 0.4.14",
|
||||
|
@ -1652,7 +1632,7 @@ name = "logins_ffi"
|
|||
version = "0.1.0"
|
||||
dependencies = [
|
||||
"base16",
|
||||
"ffi-support 0.4.2 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"ffi-support",
|
||||
"lazy_static",
|
||||
"log 0.4.14",
|
||||
"logins",
|
||||
|
@ -2229,7 +2209,7 @@ dependencies = [
|
|||
"dogear",
|
||||
"env_logger",
|
||||
"error-support",
|
||||
"ffi-support 0.4.2 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"ffi-support",
|
||||
"idna",
|
||||
"interrupt-support",
|
||||
"lazy_static",
|
||||
|
@ -2269,7 +2249,7 @@ dependencies = [
|
|||
name = "places-ffi"
|
||||
version = "0.1.0"
|
||||
dependencies = [
|
||||
"ffi-support 0.4.2 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"ffi-support",
|
||||
"interrupt-support",
|
||||
"lazy_static",
|
||||
"log 0.4.14",
|
||||
|
@ -2469,7 +2449,7 @@ dependencies = [
|
|||
"base64 0.12.3",
|
||||
"bincode",
|
||||
"error-support",
|
||||
"ffi-support 0.4.2 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"ffi-support",
|
||||
"hex",
|
||||
"lazy_static",
|
||||
"log 0.4.14",
|
||||
|
@ -2493,7 +2473,7 @@ name = "push-ffi"
|
|||
version = "0.1.0"
|
||||
dependencies = [
|
||||
"base64 0.12.3",
|
||||
"ffi-support 0.4.2 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"ffi-support",
|
||||
"lazy_static",
|
||||
"log 0.4.14",
|
||||
"prost",
|
||||
|
@ -2780,7 +2760,7 @@ name = "rc_log_ffi"
|
|||
version = "0.1.0"
|
||||
dependencies = [
|
||||
"cfg-if 0.1.10",
|
||||
"ffi-support 0.4.2 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"ffi-support",
|
||||
"lazy_static",
|
||||
"log 0.4.14",
|
||||
]
|
||||
|
@ -3194,7 +3174,7 @@ dependencies = [
|
|||
name = "sql-support"
|
||||
version = "0.1.0"
|
||||
dependencies = [
|
||||
"ffi-support 0.4.2 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"ffi-support",
|
||||
"interrupt-support",
|
||||
"lazy_static",
|
||||
"log 0.4.14",
|
||||
|
@ -3268,7 +3248,7 @@ dependencies = [
|
|||
"base64 0.12.3",
|
||||
"env_logger",
|
||||
"error-support",
|
||||
"ffi-support 0.4.2 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"ffi-support",
|
||||
"interrupt-support",
|
||||
"lazy_static",
|
||||
"log 0.4.14",
|
||||
|
@ -3288,7 +3268,7 @@ name = "sync15-traits"
|
|||
version = "0.1.0"
|
||||
dependencies = [
|
||||
"anyhow",
|
||||
"ffi-support 0.4.2 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"ffi-support",
|
||||
"log 0.4.14",
|
||||
"serde",
|
||||
"serde_json",
|
||||
|
@ -3303,7 +3283,7 @@ dependencies = [
|
|||
"anyhow",
|
||||
"autofill",
|
||||
"error-support",
|
||||
"ffi-support 0.4.2 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"ffi-support",
|
||||
"interrupt-support",
|
||||
"lazy_static",
|
||||
"log 0.4.14",
|
||||
|
@ -3325,7 +3305,7 @@ dependencies = [
|
|||
name = "sync_manager_ffi"
|
||||
version = "0.1.0"
|
||||
dependencies = [
|
||||
"ffi-support 0.4.2 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"ffi-support",
|
||||
"log 0.4.14",
|
||||
"logins_ffi",
|
||||
"places-ffi",
|
||||
|
@ -3391,7 +3371,7 @@ version = "0.1.0"
|
|||
dependencies = [
|
||||
"anyhow",
|
||||
"error-support",
|
||||
"ffi-support 0.4.2 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"ffi-support",
|
||||
"interrupt-support",
|
||||
"log 0.4.14",
|
||||
"prost",
|
||||
|
@ -3410,7 +3390,7 @@ name = "tabs_ffi"
|
|||
version = "0.1.0"
|
||||
dependencies = [
|
||||
"base16",
|
||||
"ffi-support 0.4.2 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"ffi-support",
|
||||
"lazy_static",
|
||||
"log 0.4.14",
|
||||
"prost",
|
||||
|
@ -3752,7 +3732,7 @@ dependencies = [
|
|||
"anyhow",
|
||||
"bytes 1.0.1",
|
||||
"cargo_metadata",
|
||||
"ffi-support 0.4.2 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"ffi-support",
|
||||
"lazy_static",
|
||||
"log 0.4.14",
|
||||
"paste 1.0.5",
|
||||
|
@ -3842,7 +3822,7 @@ checksum = "5fecdca9a5291cc2b8dcf7dc02453fee791a280f3743cb0905f8822ae463b3fe"
|
|||
name = "viaduct"
|
||||
version = "0.1.0"
|
||||
dependencies = [
|
||||
"ffi-support 0.4.2 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"ffi-support",
|
||||
"log 0.4.14",
|
||||
"once_cell",
|
||||
"prost",
|
||||
|
@ -3857,7 +3837,7 @@ dependencies = [
|
|||
name = "viaduct-reqwest"
|
||||
version = "0.1.0"
|
||||
dependencies = [
|
||||
"ffi-support 0.4.2 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"ffi-support",
|
||||
"lazy_static",
|
||||
"log 0.4.14",
|
||||
"reqwest",
|
||||
|
@ -3992,7 +3972,7 @@ version = "0.1.0"
|
|||
dependencies = [
|
||||
"env_logger",
|
||||
"error-support",
|
||||
"ffi-support 0.4.2 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"ffi-support",
|
||||
"interrupt-support",
|
||||
"lazy_static",
|
||||
"libsqlite3-sys",
|
||||
|
@ -4015,7 +3995,7 @@ dependencies = [
|
|||
name = "webext-storage-ffi"
|
||||
version = "0.1.0"
|
||||
dependencies = [
|
||||
"ffi-support 0.4.2 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"ffi-support",
|
||||
"lazy_static",
|
||||
"log 0.4.14",
|
||||
"serde_json",
|
||||
|
|
|
@ -13,7 +13,6 @@ members = [
|
|||
"components/push/ffi",
|
||||
"components/rc_log",
|
||||
"components/support/error",
|
||||
"components/support/ffi",
|
||||
"components/support/guid",
|
||||
"components/support/interrupt",
|
||||
"components/support/jwcrypto",
|
||||
|
@ -91,7 +90,6 @@ default-members = [
|
|||
"components/push/ffi",
|
||||
"components/rc_log",
|
||||
"components/support/error",
|
||||
"components/support/ffi",
|
||||
"components/support/guid",
|
||||
"components/support/interrupt",
|
||||
"components/support/restmail-client",
|
||||
|
|
|
@ -81,8 +81,6 @@ code in this repository
|
|||
* [rc_log](components/rc_log) - for connecting component log output to the
|
||||
application's log stream
|
||||
* [support](components/support) - low-level utility libraries
|
||||
* [support/ffi](components/support/ffi) - utilities for building a component's
|
||||
FFI bindings
|
||||
* [support/rc_crypto](components/rc_crypto) - handles cryptographic needs backed by Mozilla's
|
||||
[NSS](https://developer.mozilla.org/en-US/docs/Mozilla/Projects/NSS) library
|
||||
* [support/sql](components/support/sql) - utilities for storing data locally
|
||||
|
|
|
@ -108,8 +108,8 @@ The relevant directories are as follows:
|
|||
for syncing, displaying, and editing logins using the code in `src`. You can run it via
|
||||
cargo like so: `cargo run --example sync_pass_sql`.
|
||||
- [`ffi`](./ffi): The Rust public FFI bindings. This is a (memory-unsafe, by necessity)
|
||||
API that is exposed to Kotlin and Swift. It leverages the [`ffi_support`](../support/ffi/README.md) crate
|
||||
to avoid many issues and make it more safe than it otherwise would be. At the
|
||||
API that is exposed to Kotlin and Swift. It leverages the [`ffi_support`](https://github.com/mozilla/ffi-support)
|
||||
crate to avoid many issues and make it more safe than it otherwise would be. At the
|
||||
time of this writing, it uses JSON for marshalling data over the FFI, however
|
||||
in the future we will likely use protocol buffers.
|
||||
- [`android`](./android): This contains android bindings to logins, written in Kotlin. These
|
||||
|
|
|
@ -1,27 +0,0 @@
|
|||
[package]
|
||||
name = "ffi-support"
|
||||
edition = "2018"
|
||||
version = "0.4.2"
|
||||
authors = ["Thom Chiovoloni <tchiovoloni@mozilla.com>"]
|
||||
description = "A crate to help expose Rust functions over the FFI."
|
||||
repository = "https://github.com/mozilla/application-services"
|
||||
readme = "README.md"
|
||||
keywords = ["ffi", "bindings"]
|
||||
categories = ["development-tools::ffi"]
|
||||
license = "Apache-2.0 / MIT"
|
||||
|
||||
[badges]
|
||||
travis-ci = { repository = "mozilla/application-services" }
|
||||
|
||||
[features]
|
||||
default = []
|
||||
log_panics = []
|
||||
log_backtraces = ["log_panics", "backtrace"]
|
||||
|
||||
[dependencies]
|
||||
log = "0.4"
|
||||
lazy_static = "1.4"
|
||||
|
||||
[dependencies.backtrace]
|
||||
optional = true
|
||||
version = "0.3.48"
|
|
@ -1,201 +0,0 @@
|
|||
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.
|
|
@ -1,25 +0,0 @@
|
|||
Copyright (c) 2018-2019 Mozilla Foundation
|
||||
|
||||
Permission is hereby granted, free of charge, to any
|
||||
person obtaining a copy of this software and associated
|
||||
documentation files (the "Software"), to deal in the
|
||||
Software without restriction, including without
|
||||
limitation the rights to use, copy, modify, merge,
|
||||
publish, distribute, sublicense, and/or sell copies of
|
||||
the Software, and to permit persons to whom the Software
|
||||
is furnished to do so, subject to the following
|
||||
conditions:
|
||||
|
||||
The above copyright notice and this permission notice
|
||||
shall be included in all copies or substantial portions
|
||||
of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF
|
||||
ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED
|
||||
TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A
|
||||
PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT
|
||||
SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
|
||||
CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
|
||||
OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR
|
||||
IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
|
||||
DEALINGS IN THE SOFTWARE.
|
|
@ -1,32 +0,0 @@
|
|||
# FFI Support
|
||||
|
||||
[![Docs](https://docs.rs/ffi-support/badge.svg)](https://docs.rs/ffi-support)
|
||||
|
||||
This crate implements a support library to simplify implementing the patterns that the [mozilla/application-services](https://github.com/mozilla/application-services) repository uses for it's "Rust Component" FFI libraries, which are used to share Rust code
|
||||
|
||||
In particular, it can assist with the following areas:
|
||||
|
||||
1. Avoiding throwing panics over the FFI (which is undefined behavior)
|
||||
2. Translating rust errors (and panics) into errors that the caller on the other side of the FFI is able to handle.
|
||||
3. Converting strings to/from rust str.
|
||||
4. Passing non-string data (in a few ways, including exposing an opaque pointeer, marshalling data to JSON strings with serde, as well as arbitrary custom handling) back and forth between Rust and whatever the caller on the other side of the FFI is.
|
||||
|
||||
Additionally, it's documentation describes a number of the problems we've hit doing this to expose libraries to consumers on mobile platforms.
|
||||
|
||||
## Usage
|
||||
|
||||
Add the following to your Cargo.toml
|
||||
|
||||
```toml
|
||||
ffi-support = "0.1.1"
|
||||
```
|
||||
|
||||
For further examples, the examples in the docs is the best starting point, followed by the usage code in the [mozilla/application-services](https://github.com/mozilla/application-services) repo (for example [here](https://github.com/mozilla/application-services/blob/main/components/places/ffi/src/lib.rs) or [here](https://github.com/mozilla/application-services/blob/main/components/places/src/ffi.rs)).
|
||||
|
||||
## License
|
||||
|
||||
Dual licensed under the Apache License, Version 2.0 <LICENSE-APACHE> or
|
||||
<http://www.apache.org/licenses/LICENSE-2.0> or the MIT license <LICENSE-MIT> or
|
||||
<http://opensource.org/licenses/MIT>, at your option. All files in the project
|
||||
carrying such notice may not be copied, modified, or distributed except
|
||||
according to those terms.
|
|
@ -1,365 +0,0 @@
|
|||
/* Copyright 2018-2019 Mozilla Foundation
|
||||
*
|
||||
* Licensed under the Apache License (Version 2.0), or the MIT license,
|
||||
* (the "Licenses") at your option. You may not use this file except in
|
||||
* compliance with one of the Licenses. You may obtain copies of the
|
||||
* Licenses at:
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
* http://opensource.org/licenses/MIT
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the Licenses is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the Licenses for the specific language governing permissions and
|
||||
* limitations under the Licenses. */
|
||||
|
||||
use crate::string::{destroy_c_string, rust_string_to_c};
|
||||
use std::os::raw::c_char;
|
||||
use std::{self, ptr};
|
||||
|
||||
/// Represents an error that occured within rust, storing both an error code, and additional data
|
||||
/// that may be used by the caller.
|
||||
///
|
||||
/// Misuse of this type can cause numerous issues, so please read the entire documentation before
|
||||
/// usage.
|
||||
///
|
||||
/// ## Rationale
|
||||
///
|
||||
/// This library encourages a pattern of taking a `&mut ExternError` as the final parameter for
|
||||
/// functions exposed over the FFI. This is an "out parameter" which we use to write error/success
|
||||
/// information that occurred during the function's execution.
|
||||
///
|
||||
/// To be clear, this means instances of `ExternError` will be created on the other side of the FFI,
|
||||
/// and passed (by mutable reference) into Rust.
|
||||
///
|
||||
/// While this pattern is not particularly ergonomic in Rust (although hopefully this library
|
||||
/// helps!), it offers two main benefits over something more ergonomic (which might be `Result`
|
||||
/// shaped).
|
||||
///
|
||||
/// 1. It avoids defining a large number of `Result`-shaped types in the FFI consumer, as would
|
||||
/// be required with something like an `struct ExternResult<T> { ok: *mut T, err:... }`
|
||||
///
|
||||
/// 2. It offers additional type safety over `struct ExternResult { ok: *mut c_void, err:... }`,
|
||||
/// which helps avoid memory safety errors. It also can offer better performance for returning
|
||||
/// primitives and repr(C) structs (no boxing required).
|
||||
///
|
||||
/// It also is less tricky to use properly than giving consumers a `get_last_error()` function, or
|
||||
/// similar.
|
||||
///
|
||||
/// ## Caveats
|
||||
///
|
||||
/// Note that the order of the fields is `code` (an i32) then `message` (a `*mut c_char`), getting
|
||||
/// this wrong on the other side of the FFI will cause memory corruption and crashes.
|
||||
///
|
||||
/// The fields are public largely for documentation purposes, but you should use
|
||||
/// [`ExternError::new_error`] or [`ExternError::success`] to create these.
|
||||
///
|
||||
/// ## Layout/fields
|
||||
///
|
||||
/// This struct's field are not `pub` (mostly so that we can soundly implement `Send`, but also so
|
||||
/// that we can verify rust users are constructing them appropriately), the fields, their types, and
|
||||
/// their order are *very much* a part of the public API of this type. Consumers on the other side
|
||||
/// of the FFI will need to know its layout.
|
||||
///
|
||||
/// If this were a C struct, it would look like
|
||||
///
|
||||
/// ```c,no_run
|
||||
/// struct ExternError {
|
||||
/// int32_t code;
|
||||
/// char *message; // note: nullable
|
||||
/// };
|
||||
/// ```
|
||||
///
|
||||
/// In rust, there are two fields, in this order: `code: ErrorCode`, and `message: *mut c_char`.
|
||||
/// Note that ErrorCode is a `#[repr(transparent)]` wrapper around an `i32`, so the first property
|
||||
/// is equivalent to an `i32`.
|
||||
///
|
||||
/// #### The `code` field.
|
||||
///
|
||||
/// This is the error code, 0 represents success, all other values represent failure. If the `code`
|
||||
/// field is nonzero, there should always be a message, and if it's zero, the message will always be
|
||||
/// null.
|
||||
///
|
||||
/// #### The `message` field.
|
||||
///
|
||||
/// This is a null-terminated C string containing some amount of additional information about the
|
||||
/// error. If the `code` property is nonzero, there should always be an error message. Otherwise,
|
||||
/// this should will be null.
|
||||
///
|
||||
/// This string (when not null) is allocated on the rust heap (using this crate's
|
||||
/// [`rust_string_to_c`]), and must be freed on it as well. Critically, if there are multiple rust
|
||||
/// packages using being used in the same application, it *must be freed on the same heap that
|
||||
/// allocated it*, or you will corrupt both heaps.
|
||||
///
|
||||
/// Typically, this object is managed on the other side of the FFI (on the "FFI consumer"), which
|
||||
/// means you must expose a function to release the resources of `message` which can be done easily
|
||||
/// using the [`define_string_destructor!`] macro provided by this crate.
|
||||
///
|
||||
/// If, for some reason, you need to release the resources directly, you may call
|
||||
/// `ExternError::release()`. Note that you probably do not need to do this, and it's
|
||||
/// intentional that this is not called automatically by implementing `drop`.
|
||||
///
|
||||
/// ## Example
|
||||
///
|
||||
/// ```rust,no_run
|
||||
/// use ffi_support::{ExternError, ErrorCode};
|
||||
///
|
||||
/// #[derive(Debug)]
|
||||
/// pub enum MyError {
|
||||
/// IllegalFoo(String),
|
||||
/// InvalidBar(i64),
|
||||
/// // ...
|
||||
/// }
|
||||
///
|
||||
/// // Putting these in a module is obviously optional, but it allows documentation, and helps
|
||||
/// // avoid accidental reuse.
|
||||
/// pub mod error_codes {
|
||||
/// // note: -1 and 0 are reserved by ffi_support
|
||||
/// pub const ILLEGAL_FOO: i32 = 1;
|
||||
/// pub const INVALID_BAR: i32 = 2;
|
||||
/// // ...
|
||||
/// }
|
||||
///
|
||||
/// fn get_code(e: &MyError) -> ErrorCode {
|
||||
/// match e {
|
||||
/// MyError::IllegalFoo(_) => ErrorCode::new(error_codes::ILLEGAL_FOO),
|
||||
/// MyError::InvalidBar(_) => ErrorCode::new(error_codes::INVALID_BAR),
|
||||
/// // ...
|
||||
/// }
|
||||
/// }
|
||||
///
|
||||
/// impl From<MyError> for ExternError {
|
||||
/// fn from(e: MyError) -> ExternError {
|
||||
/// ExternError::new_error(get_code(&e), format!("{:?}", e))
|
||||
/// }
|
||||
/// }
|
||||
/// ```
|
||||
#[repr(C)]
|
||||
// Note: We're intentionally not implementing Clone -- it's too risky.
|
||||
#[derive(Debug, PartialEq)]
|
||||
pub struct ExternError {
|
||||
// Don't reorder or add anything here!
|
||||
code: ErrorCode,
|
||||
message: *mut c_char,
|
||||
}
|
||||
|
||||
impl std::panic::UnwindSafe for ExternError {}
|
||||
impl std::panic::RefUnwindSafe for ExternError {}
|
||||
|
||||
/// This is sound so long as our fields are private.
|
||||
unsafe impl Send for ExternError {}
|
||||
|
||||
impl ExternError {
|
||||
/// Construct an ExternError representing failure from a code and a message.
|
||||
#[inline]
|
||||
pub fn new_error(code: ErrorCode, message: impl Into<String>) -> Self {
|
||||
assert!(
|
||||
!code.is_success(),
|
||||
"Attempted to construct a success ExternError with a message"
|
||||
);
|
||||
Self {
|
||||
code,
|
||||
message: rust_string_to_c(message),
|
||||
}
|
||||
}
|
||||
|
||||
/// Returns a ExternError representing a success. Also returned by ExternError::default()
|
||||
#[inline]
|
||||
pub fn success() -> Self {
|
||||
Self {
|
||||
code: ErrorCode::SUCCESS,
|
||||
message: ptr::null_mut(),
|
||||
}
|
||||
}
|
||||
|
||||
/// Helper for the case where we aren't exposing this back over the FFI and
|
||||
/// we just want to warn if an error occurred and then release the allocated
|
||||
/// memory.
|
||||
///
|
||||
/// Typically, this is done if the error will still be detected and reported
|
||||
/// by other channels.
|
||||
///
|
||||
/// We assume we're not inside a catch_unwind, and so we wrap inside one
|
||||
/// ourselves.
|
||||
pub fn consume_and_log_if_error(self) {
|
||||
if !self.code.is_success() {
|
||||
// in practice this should never panic, but you never know...
|
||||
crate::abort_on_panic::call_with_output(|| {
|
||||
log::error!("Unhandled ExternError({:?}) {:?}", self.code, unsafe {
|
||||
crate::FfiStr::from_raw(self.message)
|
||||
});
|
||||
unsafe {
|
||||
self.manually_release();
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
/// Get the `code` property.
|
||||
#[inline]
|
||||
pub fn get_code(&self) -> ErrorCode {
|
||||
self.code
|
||||
}
|
||||
|
||||
/// Get the `message` property as a pointer to c_char.
|
||||
#[inline]
|
||||
pub fn get_raw_message(&self) -> *const c_char {
|
||||
self.message as *const _
|
||||
}
|
||||
|
||||
/// Get the `message` property as an [`FfiStr`]
|
||||
#[inline]
|
||||
pub fn get_message(&self) -> crate::FfiStr<'_> {
|
||||
// Safe because the lifetime is the same as our lifetime.
|
||||
unsafe { crate::FfiStr::from_raw(self.get_raw_message()) }
|
||||
}
|
||||
|
||||
/// Get the `message` property as a String, or None if this is not an error result.
|
||||
///
|
||||
/// ## Safety
|
||||
///
|
||||
/// You should only call this if you are certain that the other side of the FFI doesn't have a
|
||||
/// reference to this result (more specifically, to the `message` property) anywhere!
|
||||
#[inline]
|
||||
pub unsafe fn get_and_consume_message(self) -> Option<String> {
|
||||
if self.code.is_success() {
|
||||
None
|
||||
} else {
|
||||
let res = self.get_message().into_string();
|
||||
self.manually_release();
|
||||
Some(res)
|
||||
}
|
||||
}
|
||||
|
||||
/// Manually release the memory behind this string. You probably don't want to call this.
|
||||
///
|
||||
/// ## Safety
|
||||
///
|
||||
/// You should only call this if you are certain that the other side of the FFI doesn't have a
|
||||
/// reference to this result (more specifically, to the `message` property) anywhere!
|
||||
pub unsafe fn manually_release(self) {
|
||||
if !self.message.is_null() {
|
||||
destroy_c_string(self.message)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl Default for ExternError {
|
||||
#[inline]
|
||||
fn default() -> Self {
|
||||
ExternError::success()
|
||||
}
|
||||
}
|
||||
|
||||
// This is the `Err` of std::thread::Result, which is what
|
||||
// `panic::catch_unwind` returns.
|
||||
impl From<Box<dyn std::any::Any + Send + 'static>> for ExternError {
|
||||
fn from(e: Box<dyn std::any::Any + Send + 'static>) -> Self {
|
||||
// The documentation suggests that it will *usually* be a str or String.
|
||||
let message = if let Some(s) = e.downcast_ref::<&'static str>() {
|
||||
(*s).to_string()
|
||||
} else if let Some(s) = e.downcast_ref::<String>() {
|
||||
s.clone()
|
||||
} else {
|
||||
"Unknown panic!".to_string()
|
||||
};
|
||||
log::error!("Caught a panic calling rust code: {:?}", message);
|
||||
ExternError::new_error(ErrorCode::PANIC, message)
|
||||
}
|
||||
}
|
||||
|
||||
/// A wrapper around error codes, which is represented identically to an i32 on the other side of
|
||||
/// the FFI. Essentially exists to check that we don't accidentally reuse success/panic codes for
|
||||
/// other things.
|
||||
#[repr(transparent)]
|
||||
#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Default)]
|
||||
pub struct ErrorCode(i32);
|
||||
|
||||
impl ErrorCode {
|
||||
/// The ErrorCode used for success.
|
||||
pub const SUCCESS: ErrorCode = ErrorCode(0);
|
||||
|
||||
/// The ErrorCode used for panics. It's unlikely you need to ever use this.
|
||||
// TODO: Consider moving to the reserved region...
|
||||
pub const PANIC: ErrorCode = ErrorCode(-1);
|
||||
|
||||
/// The ErrorCode used for handle map errors.
|
||||
pub const INVALID_HANDLE: ErrorCode = ErrorCode(-1000);
|
||||
|
||||
/// Construct an error code from an integer code.
|
||||
///
|
||||
/// ## Panics
|
||||
///
|
||||
/// Panics if you call it with 0 (reserved for success, but you can use `ErrorCode::SUCCESS` if
|
||||
/// that's what you want), or -1 (reserved for panics, but you can use `ErrorCode::PANIC` if
|
||||
/// that's what you want).
|
||||
pub fn new(code: i32) -> Self {
|
||||
assert!(code > ErrorCode::INVALID_HANDLE.0 && code != ErrorCode::PANIC.0 && code != ErrorCode::SUCCESS.0,
|
||||
"Error: The ErrorCodes `{success}`, `{panic}`, and all error codes less than or equal \
|
||||
to `{reserved}` are reserved (got {code}). You may use the associated constants on this \
|
||||
type (`ErrorCode::PANIC`, etc) if you'd like instances of those error codes.",
|
||||
panic = ErrorCode::PANIC.0,
|
||||
success = ErrorCode::SUCCESS.0,
|
||||
reserved = ErrorCode::INVALID_HANDLE.0,
|
||||
code = code,
|
||||
);
|
||||
|
||||
ErrorCode(code)
|
||||
}
|
||||
|
||||
/// Get the raw numeric value of this ErrorCode.
|
||||
#[inline]
|
||||
pub fn code(self) -> i32 {
|
||||
self.0
|
||||
}
|
||||
|
||||
/// Returns whether or not this is a success code.
|
||||
#[inline]
|
||||
pub fn is_success(self) -> bool {
|
||||
self.code() == 0
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod test {
|
||||
use super::*;
|
||||
|
||||
#[test]
|
||||
#[should_panic]
|
||||
fn test_code_new_reserved_success() {
|
||||
ErrorCode::new(0);
|
||||
}
|
||||
|
||||
#[test]
|
||||
#[should_panic]
|
||||
fn test_code_new_reserved_panic() {
|
||||
ErrorCode::new(-1);
|
||||
}
|
||||
|
||||
#[test]
|
||||
#[should_panic]
|
||||
fn test_code_new_reserved_handle_error() {
|
||||
ErrorCode::new(-1000);
|
||||
}
|
||||
#[test]
|
||||
#[should_panic]
|
||||
fn test_code_new_reserved_unknown() {
|
||||
// Everything below -1000 should be reserved.
|
||||
ErrorCode::new(-1043);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_code_new_allowed() {
|
||||
// Should not panic
|
||||
ErrorCode::new(-2);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_code() {
|
||||
assert!(!ErrorCode::PANIC.is_success());
|
||||
assert!(!ErrorCode::INVALID_HANDLE.is_success());
|
||||
assert!(ErrorCode::SUCCESS.is_success());
|
||||
assert_eq!(ErrorCode::default(), ErrorCode::SUCCESS);
|
||||
}
|
||||
}
|
|
@ -1,252 +0,0 @@
|
|||
/* Copyright 2018-2019 Mozilla Foundation
|
||||
*
|
||||
* Licensed under the Apache License (Version 2.0), or the MIT license,
|
||||
* (the "Licenses") at your option. You may not use this file except in
|
||||
* compliance with one of the Licenses. You may obtain copies of the
|
||||
* Licenses at:
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
* http://opensource.org/licenses/MIT
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the Licenses is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the Licenses for the specific language governing permissions and
|
||||
* limitations under the Licenses. */
|
||||
|
||||
use std::ffi::CStr;
|
||||
use std::marker::PhantomData;
|
||||
use std::os::raw::c_char;
|
||||
|
||||
/// `FfiStr<'a>` is a safe (`#[repr(transparent)]`) wrapper around a
|
||||
/// nul-terminated `*const c_char` (e.g. a C string). Conceptually, it is
|
||||
/// similar to [`std::ffi::CStr`], except that it may be used in the signatures
|
||||
/// of extern "C" functions.
|
||||
///
|
||||
/// Functions accepting strings should use this instead of accepting a C string
|
||||
/// directly. This allows us to write those functions using safe code without
|
||||
/// allowing safe Rust to cause memory unsafety.
|
||||
///
|
||||
/// A single function for constructing these from Rust ([`FfiStr::from_raw`])
|
||||
/// has been provided. Most of the time, this should not be necessary, and users
|
||||
/// should accept `FfiStr` in the parameter list directly.
|
||||
///
|
||||
/// ## Caveats
|
||||
///
|
||||
/// An effort has been made to make this struct hard to misuse, however it is
|
||||
/// still possible, if the `'static` lifetime is manually specified in the
|
||||
/// struct. E.g.
|
||||
///
|
||||
/// ```rust,no_run
|
||||
/// # use ffi_support::FfiStr;
|
||||
/// // NEVER DO THIS
|
||||
/// #[no_mangle]
|
||||
/// extern "C" fn never_do_this(s: FfiStr<'static>) {
|
||||
/// // save `s` somewhere, and access it after this
|
||||
/// // function returns.
|
||||
/// }
|
||||
/// ```
|
||||
///
|
||||
/// Instead, one of the following patterns should be used:
|
||||
///
|
||||
/// ```
|
||||
/// # use ffi_support::FfiStr;
|
||||
/// #[no_mangle]
|
||||
/// extern "C" fn valid_use_1(s: FfiStr<'_>) {
|
||||
/// // Use of `s` after this function returns is impossible
|
||||
/// }
|
||||
/// // Alternative:
|
||||
/// #[no_mangle]
|
||||
/// extern "C" fn valid_use_2(s: FfiStr) {
|
||||
/// // Use of `s` after this function returns is impossible
|
||||
/// }
|
||||
/// ```
|
||||
#[repr(transparent)]
|
||||
pub struct FfiStr<'a> {
|
||||
cstr: *const c_char,
|
||||
_boo: PhantomData<&'a ()>,
|
||||
}
|
||||
|
||||
impl<'a> FfiStr<'a> {
|
||||
/// Construct an `FfiStr` from a raw pointer.
|
||||
///
|
||||
/// This should not be needed most of the time, and users should instead
|
||||
/// accept `FfiStr` in function parameter lists.
|
||||
///
|
||||
/// # Safety
|
||||
///
|
||||
/// Dereferences a pointer and is thus unsafe.
|
||||
#[inline]
|
||||
pub unsafe fn from_raw(ptr: *const c_char) -> Self {
|
||||
Self {
|
||||
cstr: ptr,
|
||||
_boo: PhantomData,
|
||||
}
|
||||
}
|
||||
|
||||
/// Construct a FfiStr from a `std::ffi::CStr`. This is provided for
|
||||
/// completeness, as a safe method of producing an `FfiStr` in Rust.
|
||||
#[inline]
|
||||
pub fn from_cstr(cstr: &'a CStr) -> Self {
|
||||
Self {
|
||||
cstr: cstr.as_ptr(),
|
||||
_boo: PhantomData,
|
||||
}
|
||||
}
|
||||
|
||||
/// Get an `&str` out of the `FfiStr`. This will panic in any case that
|
||||
/// [`FfiStr::as_opt_str`] would return `None` (e.g. null pointer or invalid
|
||||
/// UTF-8).
|
||||
///
|
||||
/// If the string should be optional, you should use [`FfiStr::as_opt_str`]
|
||||
/// instead. If an owned string is desired, use [`FfiStr::into_string`] or
|
||||
/// [`FfiStr::into_opt_string`].
|
||||
#[inline]
|
||||
pub fn as_str(&self) -> &'a str {
|
||||
self.as_opt_str()
|
||||
.expect("Unexpected null string pointer passed to rust")
|
||||
}
|
||||
|
||||
/// Get an `Option<&str>` out of the `FfiStr`. If this stores a null
|
||||
/// pointer, then None will be returned. If a string containing invalid
|
||||
/// UTF-8 was passed, then an error will be logged and `None` will be
|
||||
/// returned.
|
||||
///
|
||||
/// If the string is a required argument, use [`FfiStr::as_str`], or
|
||||
/// [`FfiStr::into_string`] instead. If `Option<String>` is desired, use
|
||||
/// [`FfiStr::into_opt_string`] (which will handle invalid UTF-8 by
|
||||
/// replacing with the replacement character).
|
||||
pub fn as_opt_str(&self) -> Option<&'a str> {
|
||||
if self.cstr.is_null() {
|
||||
return None;
|
||||
}
|
||||
unsafe {
|
||||
match std::ffi::CStr::from_ptr(self.cstr).to_str() {
|
||||
Ok(s) => Some(s),
|
||||
Err(e) => {
|
||||
log::error!("Invalid UTF-8 was passed to rust! {:?}", e);
|
||||
None
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Get an `Option<String>` out of the `FfiStr`. Returns `None` if this
|
||||
/// `FfiStr` holds a null pointer. Note that unlike [`FfiStr::as_opt_str`],
|
||||
/// invalid UTF-8 is replaced with the replacement character instead of
|
||||
/// causing us to return None.
|
||||
///
|
||||
/// If the string should be mandatory, you should use
|
||||
/// [`FfiStr::into_string`] instead. If an owned string is not needed, you
|
||||
/// may want to use [`FfiStr::as_str`] or [`FfiStr::as_opt_str`] instead,
|
||||
/// (however, note the differences in how invalid UTF-8 is handled, should
|
||||
/// this be relevant to your use).
|
||||
pub fn into_opt_string(self) -> Option<String> {
|
||||
if !self.cstr.is_null() {
|
||||
unsafe { Some(CStr::from_ptr(self.cstr).to_string_lossy().to_string()) }
|
||||
} else {
|
||||
None
|
||||
}
|
||||
}
|
||||
|
||||
/// Get a `String` out of a `FfiStr`. This function is essential a
|
||||
/// convenience wrapper for `ffi_str.into_opt_string().unwrap()`, with a
|
||||
/// message that indicates that a null argument was passed to rust when it
|
||||
/// should be mandatory. As with [`FfiStr::into_opt_string`], invalid UTF-8
|
||||
/// is replaced with the replacement character if encountered.
|
||||
///
|
||||
/// If the string should *not* be mandatory, you should use
|
||||
/// [`FfiStr::into_opt_string`] instead. If an owned string is not needed,
|
||||
/// you may want to use [`FfiStr::as_str`] or [`FfiStr::as_opt_str`]
|
||||
/// instead, (however, note the differences in how invalid UTF-8 is handled,
|
||||
/// should this be relevant to your use).
|
||||
#[inline]
|
||||
pub fn into_string(self) -> String {
|
||||
self.into_opt_string()
|
||||
.expect("Unexpected null string pointer passed to rust")
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a> std::fmt::Debug for FfiStr<'a> {
|
||||
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
||||
if let Some(s) = self.as_opt_str() {
|
||||
write!(f, "FfiStr({:?})", s)
|
||||
} else {
|
||||
write!(f, "FfiStr(null)")
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Conversions...
|
||||
|
||||
impl<'a> From<FfiStr<'a>> for String {
|
||||
#[inline]
|
||||
fn from(f: FfiStr<'a>) -> Self {
|
||||
f.into_string()
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a> From<FfiStr<'a>> for Option<String> {
|
||||
#[inline]
|
||||
fn from(f: FfiStr<'a>) -> Self {
|
||||
f.into_opt_string()
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a> From<FfiStr<'a>> for Option<&'a str> {
|
||||
#[inline]
|
||||
fn from(f: FfiStr<'a>) -> Self {
|
||||
f.as_opt_str()
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a> From<FfiStr<'a>> for &'a str {
|
||||
#[inline]
|
||||
fn from(f: FfiStr<'a>) -> Self {
|
||||
f.as_str()
|
||||
}
|
||||
}
|
||||
|
||||
// TODO: `AsRef<str>`?
|
||||
|
||||
// Comparisons...
|
||||
|
||||
// Compare FfiStr with eachother
|
||||
impl<'a> PartialEq for FfiStr<'a> {
|
||||
#[inline]
|
||||
fn eq(&self, other: &FfiStr<'a>) -> bool {
|
||||
self.as_opt_str() == other.as_opt_str()
|
||||
}
|
||||
}
|
||||
|
||||
// Compare FfiStr with str
|
||||
impl<'a> PartialEq<str> for FfiStr<'a> {
|
||||
#[inline]
|
||||
fn eq(&self, other: &str) -> bool {
|
||||
self.as_opt_str() == Some(other)
|
||||
}
|
||||
}
|
||||
|
||||
// Compare FfiStr with &str
|
||||
impl<'a, 'b> PartialEq<&'b str> for FfiStr<'a> {
|
||||
#[inline]
|
||||
fn eq(&self, other: &&'b str) -> bool {
|
||||
self.as_opt_str() == Some(*other)
|
||||
}
|
||||
}
|
||||
|
||||
// rhs/lhs swap version of above
|
||||
impl<'a> PartialEq<FfiStr<'a>> for str {
|
||||
#[inline]
|
||||
fn eq(&self, other: &FfiStr<'a>) -> bool {
|
||||
Some(self) == other.as_opt_str()
|
||||
}
|
||||
}
|
||||
|
||||
// rhs/lhs swap...
|
||||
impl<'a, 'b> PartialEq<FfiStr<'a>> for &'b str {
|
||||
#[inline]
|
||||
fn eq(&self, other: &FfiStr<'a>) -> bool {
|
||||
Some(*self) == other.as_opt_str()
|
||||
}
|
||||
}
|
Разница между файлами не показана из-за своего большого размера
Загрузить разницу
|
@ -1,280 +0,0 @@
|
|||
/* Copyright 2018-2019 Mozilla Foundation
|
||||
*
|
||||
* Licensed under the Apache License (Version 2.0), or the MIT license,
|
||||
* (the "Licenses") at your option. You may not use this file except in
|
||||
* compliance with one of the Licenses. You may obtain copies of the
|
||||
* Licenses at:
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
* http://opensource.org/licenses/MIT
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the Licenses is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the Licenses for the specific language governing permissions and
|
||||
* limitations under the Licenses. */
|
||||
|
||||
use crate::string::*;
|
||||
use std::os::raw::c_char;
|
||||
use std::ptr;
|
||||
|
||||
/// This trait is used to return types over the FFI. It essentially is a mapping between a type and
|
||||
/// version of that type we can pass back to C (`IntoFfi::Value`).
|
||||
///
|
||||
/// The main wrinkle is that we need to be able to pass a value back to C in both the success and
|
||||
/// error cases. In the error cases, we don't want there to need to be any cleanup for the foreign
|
||||
/// code to do, and we want the API to be relatively easy to use.
|
||||
///
|
||||
/// Additionally, the mapping is not consistent for different types. For some rust types, we want to
|
||||
/// convert them to JSON. For some, we want to return an opaque `*mut T` handle. For others,
|
||||
/// we'd like to return by value.
|
||||
///
|
||||
/// This trait supports those cases by adding some type-level indirection, and allowing both cases
|
||||
/// to be provided (both cases what is done in the error and success cases).
|
||||
///
|
||||
/// We implement this for the following types:
|
||||
///
|
||||
/// - `String`, by conversion to `*mut c_char`. Note that the caller (on the other side of the FFI)
|
||||
/// is expected to free this, so you will need to provide them with a destructor for strings,
|
||||
/// which can be done with the [`define_string_destructor!`] macro.
|
||||
///
|
||||
/// - `()`: as a no-op conversion -- this just allows us to expose functions without a return type
|
||||
/// over the FFI.
|
||||
///
|
||||
/// - `bool`: is implemented by conversion to `u8` (`0u8` is `false`, `1u8` is `true`, and
|
||||
/// `ffi_default()` is `false`). This is because it doesn't seem to be safe to pass over the FFI
|
||||
/// directly (or at least, doing so might hit a bug in JNA).
|
||||
///
|
||||
/// - All numeric primitives except `isize`, `usize`, `char`, `i128`, and `u128` are implememented
|
||||
/// by passing directly through (and using `Default::default()` for `ffi_default()`).
|
||||
/// - `isize`, `usize` could be added, but they'd be quite easy to accidentally misuse, so we
|
||||
/// currently omit them.
|
||||
/// - `char` is less easy to misuse, but it's also less clear why you'd want to be doing this.
|
||||
/// If we did ever add this, we'd probably want to convert to a `u32` (similar to how we
|
||||
/// convert `bool` to `u8`) for better ABI stability.
|
||||
/// - `i128` and `u128` do not have a stable ABI, so they cannot be returned across the FFI.
|
||||
///
|
||||
/// - `Option<T>` where `T` is `IntoFfi`, by returning `IntoFfi::ffi_default()` for `None`.
|
||||
///
|
||||
/// None of these are directly helpful for user types though, so macros are provided for the
|
||||
/// following cases:
|
||||
///
|
||||
/// 1. For types which are passed around by an opaque pointer, the macro
|
||||
/// [`implement_into_ffi_by_pointer!`] is provided.
|
||||
///
|
||||
/// 2. For types which should be returned as a JSON string, the macro
|
||||
/// [`implement_into_ffi_by_json!`] is provided.
|
||||
///
|
||||
/// See the "Examples" section below for some other cases, such as returning by value.
|
||||
///
|
||||
/// ## Safety
|
||||
///
|
||||
/// This is an unsafe trait (implementing it requires `unsafe impl`). This is because we cannot
|
||||
/// guarantee that your type is safe to pass to C. The helpers we've provided as macros should be
|
||||
/// safe to use, and in the cases where a common pattern can't be done both safely and generically,
|
||||
/// we've opted not to provide a macro for it. That said, many of these cases are still safe if you
|
||||
/// meet some relatively basic requirements, see below for examples.
|
||||
///
|
||||
/// ## Examples
|
||||
///
|
||||
/// ### Returning types by value
|
||||
///
|
||||
/// If you want to return a type by value, we don't provide a macro for this, primarially because
|
||||
/// doing so cannot be statically guarantee that it is safe. However, it *is* safe for the cases
|
||||
/// where the type is either `#[repr(C)]` or `#[repr(transparent)]`. If this doesn't hold, you will
|
||||
/// want to use a different option!
|
||||
///
|
||||
/// Regardless, if this holds, it's fairly simple to implement, for example:
|
||||
///
|
||||
/// ```rust
|
||||
/// # use ffi_support::IntoFfi;
|
||||
/// #[derive(Default)]
|
||||
/// #[repr(C)]
|
||||
/// pub struct Point {
|
||||
/// pub x: i32,
|
||||
/// pub y: i32,
|
||||
/// }
|
||||
///
|
||||
/// unsafe impl IntoFfi for Point {
|
||||
/// type Value = Self;
|
||||
/// #[inline] fn ffi_default() -> Self { Default::default() }
|
||||
/// #[inline] fn into_ffi_value(self) -> Self { self }
|
||||
/// }
|
||||
/// ```
|
||||
///
|
||||
/// ### Conversion to another type (which is returned over the FFI)
|
||||
///
|
||||
/// In the FxA FFI, we used to have a `SyncKeys` type, which was converted to a different type before
|
||||
/// returning over the FFI. (The real FxA FFI is a little different, and more complex, but this is
|
||||
/// relatively close, and more widely recommendable than the one the FxA FFI uses):
|
||||
///
|
||||
/// This is fairly easy to do by performing the conversion inside `IntoFfi`.
|
||||
///
|
||||
/// ```rust
|
||||
/// # use ffi_support::{self, IntoFfi};
|
||||
/// # use std::{ptr, os::raw::c_char};
|
||||
/// pub struct SyncKeys(pub String, pub String);
|
||||
///
|
||||
/// #[repr(C)]
|
||||
/// pub struct SyncKeysC {
|
||||
/// pub sync_key: *mut c_char,
|
||||
/// pub xcs: *mut c_char,
|
||||
/// }
|
||||
///
|
||||
/// unsafe impl IntoFfi for SyncKeys {
|
||||
/// type Value = SyncKeysC;
|
||||
/// #[inline]
|
||||
/// fn ffi_default() -> SyncKeysC {
|
||||
/// SyncKeysC {
|
||||
/// sync_key: ptr::null_mut(),
|
||||
/// xcs: ptr::null_mut(),
|
||||
/// }
|
||||
/// }
|
||||
///
|
||||
/// #[inline]
|
||||
/// fn into_ffi_value(self) -> SyncKeysC {
|
||||
/// SyncKeysC {
|
||||
/// sync_key: ffi_support::rust_string_to_c(self.0),
|
||||
/// xcs: ffi_support::rust_string_to_c(self.1),
|
||||
/// }
|
||||
/// }
|
||||
/// }
|
||||
///
|
||||
/// // Note: this type manages memory, so you still will want to expose a destructor for this,
|
||||
/// // and possibly implement Drop as well.
|
||||
/// ```
|
||||
pub unsafe trait IntoFfi: Sized {
|
||||
/// This type must be:
|
||||
///
|
||||
/// 1. Compatible with C, which is to say `#[repr(C)]`, a numeric primitive,
|
||||
/// another type that has guarantees made about it's layout, or a
|
||||
/// `#[repr(transparent)]` wrapper around one of those.
|
||||
///
|
||||
/// One could even use `&T`, so long as `T: Sized`, although it's
|
||||
/// extremely dubious to return a reference to borrowed memory over the
|
||||
/// FFI, since it's very difficult for the caller to know how long it
|
||||
/// remains valid.
|
||||
///
|
||||
/// 2. Capable of storing an empty/ignorable/default value.
|
||||
///
|
||||
/// 3. Capable of storing the actual value.
|
||||
///
|
||||
/// Valid examples include:
|
||||
///
|
||||
/// - Primitive numbers (other than i128/u128)
|
||||
///
|
||||
/// - #[repr(C)] structs containing only things on this list.
|
||||
///
|
||||
/// - `Option<Box<T>>`, but only if `T` is `Sized`. (Internally this is
|
||||
/// guaranteed to be represented equivalently to a pointer)
|
||||
///
|
||||
/// - Raw pointers such as `*const T`, and `*mut T`, but again, only if `T`
|
||||
/// is `Sized` (`*const [T]`, `*mut dyn SomeTrait` etc are not valid).
|
||||
///
|
||||
/// - Enums with a fixed `repr`, although it's a good idea avoid
|
||||
/// `#[repr(C)]` enums in favor of, say, `#[repr(i32)]` (for example, any
|
||||
/// fixed type there should be fine), as it's potentially error prone to
|
||||
/// access `#[repr(C)]` enums from Android over JNA (it's only safe if C's
|
||||
/// `sizeof(int) == 4`, which is very common, but not universally true).
|
||||
///
|
||||
/// - `&T`/`&mut T` where `T: Sized` but only if you really know what you're
|
||||
/// doing, because this is probably a mistake.
|
||||
///
|
||||
/// Invalid examples include things like `&str`, `&[T]`, `String`, `Vec<T>`,
|
||||
/// `std::ffi::CString`, `&std::ffi::CStr`, etc.
|
||||
type Value;
|
||||
|
||||
/// Return an 'empty' value. This is what's passed back to C in the case of an error,
|
||||
/// so it doesn't actually need to be "empty", so much as "ignorable". Note that this
|
||||
/// is also used when an empty `Option<T>` is returned.
|
||||
fn ffi_default() -> Self::Value;
|
||||
|
||||
/// Convert ourselves into a value we can pass back to C with confidence.
|
||||
fn into_ffi_value(self) -> Self::Value;
|
||||
}
|
||||
|
||||
unsafe impl IntoFfi for String {
|
||||
type Value = *mut c_char;
|
||||
|
||||
#[inline]
|
||||
fn ffi_default() -> Self::Value {
|
||||
ptr::null_mut()
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn into_ffi_value(self) -> Self::Value {
|
||||
rust_string_to_c(self)
|
||||
}
|
||||
}
|
||||
|
||||
// Implement IntoFfi for Option<T> by falling back to ffi_default for None.
|
||||
unsafe impl<T: IntoFfi> IntoFfi for Option<T> {
|
||||
type Value = <T as IntoFfi>::Value;
|
||||
|
||||
#[inline]
|
||||
fn ffi_default() -> Self::Value {
|
||||
T::ffi_default()
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn into_ffi_value(self) -> Self::Value {
|
||||
if let Some(s) = self {
|
||||
s.into_ffi_value()
|
||||
} else {
|
||||
T::ffi_default()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// We've had problems in the past returning booleans over the FFI (specifically to JNA), and so
|
||||
// we convert them to `u8`.
|
||||
unsafe impl IntoFfi for bool {
|
||||
type Value = u8;
|
||||
#[inline]
|
||||
fn ffi_default() -> Self::Value {
|
||||
0u8
|
||||
}
|
||||
#[inline]
|
||||
fn into_ffi_value(self) -> Self::Value {
|
||||
self as u8
|
||||
}
|
||||
}
|
||||
|
||||
unsafe impl IntoFfi for crate::ByteBuffer {
|
||||
type Value = crate::ByteBuffer;
|
||||
#[inline]
|
||||
fn ffi_default() -> Self::Value {
|
||||
crate::ByteBuffer::default()
|
||||
}
|
||||
#[inline]
|
||||
fn into_ffi_value(self) -> Self::Value {
|
||||
self
|
||||
}
|
||||
}
|
||||
|
||||
// just cuts down on boilerplate. Not public.
|
||||
macro_rules! impl_into_ffi_for_primitive {
|
||||
($($T:ty),+) => {$(
|
||||
unsafe impl IntoFfi for $T {
|
||||
type Value = Self;
|
||||
#[inline] fn ffi_default() -> Self { Default::default() }
|
||||
#[inline] fn into_ffi_value(self) -> Self { self }
|
||||
}
|
||||
)+}
|
||||
}
|
||||
|
||||
// See IntoFfi docs for why this is not exhaustive
|
||||
impl_into_ffi_for_primitive![(), i8, u8, i16, u16, i32, u32, i64, u64, f32, f64];
|
||||
|
||||
// just cuts down on boilerplate. Not public.
|
||||
macro_rules! impl_into_ffi_for_pointer {
|
||||
($($T:ty),+) => {$(
|
||||
unsafe impl IntoFfi for $T {
|
||||
type Value = Self;
|
||||
#[inline] fn ffi_default() -> Self { ptr::null_mut() }
|
||||
#[inline] fn into_ffi_value(self) -> Self { self }
|
||||
}
|
||||
)+}
|
||||
}
|
||||
|
||||
impl_into_ffi_for_pointer![*mut i8, *const i8, *mut u8, *const u8];
|
|
@ -1,623 +0,0 @@
|
|||
/* Copyright 2018-2019 Mozilla Foundation
|
||||
*
|
||||
* Licensed under the Apache License (Version 2.0), or the MIT license,
|
||||
* (the "Licenses") at your option. You may not use this file except in
|
||||
* compliance with one of the Licenses. You may obtain copies of the
|
||||
* Licenses at:
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
* http://opensource.org/licenses/MIT
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the Licenses is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the Licenses for the specific language governing permissions and
|
||||
* limitations under the Licenses. */
|
||||
|
||||
#![deny(missing_docs)]
|
||||
#![allow(unknown_lints)]
|
||||
#![warn(rust_2018_idioms)]
|
||||
|
||||
//! # FFI Support
|
||||
//!
|
||||
//! This crate implements a support library to simplify implementing the patterns that the
|
||||
//! `mozilla/application-services` repository uses for it's "Rust Component" FFI libraries.
|
||||
//!
|
||||
//! It is *strongly encouraged* that anybody writing FFI code in this repository read this
|
||||
//! documentation before doing so, as it is a subtle, difficult, and error prone process.
|
||||
//!
|
||||
//! ## Terminology
|
||||
//!
|
||||
//! For each library, there are currently three parts we're concerned with. There's no clear correct
|
||||
//! name for these, so this documentation will attempt to use the following terminology:
|
||||
//!
|
||||
//! - **Rust Component**: A Rust crate which does not expose an FFI directly, but may be may be
|
||||
//! wrapped by one that does. These have a `crate-type` in their Cargo.toml (see
|
||||
//! https://doc.rust-lang.org/reference/linkage.html) of `lib`, and not `staticlib` or `cdylib`
|
||||
//! (Note that `lib` is the default if `crate-type` is not specified). Examples include the
|
||||
//! `fxa-client`, and `logins` crates.
|
||||
//!
|
||||
//! - **FFI Component**: A wrapper crate that takes a Rust component, and exposes an FFI from it.
|
||||
//! These typically have `ffi` in the name, and have `crate-type = ["lib", "staticlib", "cdylib"]`
|
||||
//! in their Cargo.toml. For example, the `fxa-client/ffi` and `logins/ffi` crates (note:
|
||||
//! paths are subject to change). When built, these produce a native library that is consumed by
|
||||
//! the "FFI Consumer".
|
||||
//!
|
||||
//! - **FFI Consumer**: This is a low level library, typically implemented in Kotlin (for Android)
|
||||
//! or Swift (for iOS), that exposes a memory-safe wrapper around the memory-unsafe C API produced
|
||||
//! by the FFI component. It's expected that the maintainers of the FFI Component and FFI Consumer
|
||||
//! be the same (or at least, the author of the consumer should be completely comfortable with the
|
||||
//! API exposed by, and code in the FFI component), since the code in these is extremely tightly
|
||||
//! coupled, and very easy to get wrong.
|
||||
//!
|
||||
//! Note that while there are three parts, there may be more than three libraries relevant here, for
|
||||
//! example there may be more than one FFI consumer (one for Android, one for iOS).
|
||||
//!
|
||||
//! ## Usage
|
||||
//!
|
||||
//! This library will typically be used in both the Rust component, and the FFI component, however
|
||||
//! it frequently will be an optional dependency in the Rust component that's only available when a
|
||||
//! feature flag (which the FFI component will always require) is used.
|
||||
//!
|
||||
//! The reason it's required inside the Rust component (and not solely in the FFI component, which
|
||||
//! would be nice), is so that types provided by that crate may implement the traits provided by
|
||||
//! this crate (this is because Rust does not allow crate `C` to implement a trait defined in crate
|
||||
//! `A` for a type defined in crate `B`).
|
||||
//!
|
||||
//! In general, examples should be provided for the most important types and functions
|
||||
//! ([`call_with_result`], [`IntoFfi`],
|
||||
//! [`ExternError`], etc), but you should also look at the code of
|
||||
//! consumers of this library.
|
||||
//!
|
||||
//! ### Usage in the Rust Component
|
||||
//!
|
||||
//! Inside the Rust component, you will implement:
|
||||
//!
|
||||
//! 1. [`IntoFfi`] for all types defined in that crate that you want to return
|
||||
//! over the FFI. For most common cases, the [`implement_into_ffi_by_json!`] and
|
||||
//! [`implement_into_ffi_by_protobuf!`] macros will do the job here, however you
|
||||
//! can see that trait's documentation for discussion and examples of
|
||||
//! implementing it manually.
|
||||
//!
|
||||
//! 2. Conversion to [`ExternError`] for the error type(s) exposed by that
|
||||
//! rust component, that is, `impl From<MyError> for ExternError`.
|
||||
//!
|
||||
//! ### Usage in the FFI Component
|
||||
//!
|
||||
//! Inside the FFI component, you will use this library in a few ways:
|
||||
//!
|
||||
//! 1. Destructors will be exposed for each types that had [`implement_into_ffi_by_pointer!`] called
|
||||
//! on it (using [`define_box_destructor!`]), and a destructor for strings should be exposed as
|
||||
//! well, using [`define_string_destructor`]
|
||||
//!
|
||||
//! 2. The body of every / nearly every FFI function will be wrapped in either a
|
||||
//! [`call_with_result`] or [`call_with_output`].
|
||||
//!
|
||||
//! This is required because if we `panic!` (e.g. from an `assert!`, `unwrap()`, `expect()`, from
|
||||
//! indexing past the end of an array, etc) across the FFI boundary, the behavior is undefined
|
||||
//! and in practice very weird things tend to happen (we aren't caught by the caller, since they
|
||||
//! don't have the same exception behavior as us).
|
||||
//!
|
||||
//! If you don't think your program (or possibly just certain calls) can handle panics, you may
|
||||
//! also use the versions of these functions in the [`abort_on_panic`] module, which
|
||||
//! do as their name suggest.
|
||||
//!
|
||||
//! Additionally, c strings that are passed in as arguments may be represented using [`FfiStr`],
|
||||
//! which contains several helpful inherent methods for extracting their data.
|
||||
//!
|
||||
|
||||
use std::{panic, thread};
|
||||
|
||||
mod error;
|
||||
mod ffistr;
|
||||
pub mod handle_map;
|
||||
mod into_ffi;
|
||||
#[macro_use]
|
||||
mod macros;
|
||||
mod string;
|
||||
|
||||
pub use crate::error::*;
|
||||
pub use crate::ffistr::FfiStr;
|
||||
pub use crate::into_ffi::*;
|
||||
pub use crate::macros::*;
|
||||
pub use crate::string::*;
|
||||
|
||||
// We export most of the types from this, but some constants
|
||||
// (MAX_CAPACITY) don't make sense at the top level.
|
||||
pub use crate::handle_map::{ConcurrentHandleMap, Handle, HandleError, HandleMap};
|
||||
|
||||
/// Call a callback that returns a `Result<T, E>` while:
|
||||
///
|
||||
/// - Catching panics, and reporting them to C via [`ExternError`].
|
||||
/// - Converting `T` to a C-compatible type using [`IntoFfi`].
|
||||
/// - Converting `E` to a C-compatible error via `Into<ExternError>`.
|
||||
///
|
||||
/// This (or [`call_with_output`]) should be in the majority of the FFI functions, see the crate
|
||||
/// top-level docs for more info.
|
||||
///
|
||||
/// If your function doesn't produce an error, you may use [`call_with_output`] instead, which
|
||||
/// doesn't require you return a Result.
|
||||
///
|
||||
/// ## Example
|
||||
///
|
||||
/// A few points about the following example:
|
||||
///
|
||||
/// - We need to mark it as `#[no_mangle] pub extern "C"`.
|
||||
///
|
||||
/// - We prefix it with a unique name for the library (e.g. `mylib_`). Foreign functions are not
|
||||
/// namespaced, and symbol collisions can cause a large number of problems and subtle bugs,
|
||||
/// including memory safety issues in some cases.
|
||||
///
|
||||
/// ```rust,no_run
|
||||
/// # use ffi_support::{ExternError, ErrorCode, FfiStr};
|
||||
/// # use std::os::raw::c_char;
|
||||
///
|
||||
/// # #[derive(Debug)]
|
||||
/// # struct BadEmptyString;
|
||||
/// # impl From<BadEmptyString> for ExternError {
|
||||
/// # fn from(e: BadEmptyString) -> Self {
|
||||
/// # ExternError::new_error(ErrorCode::new(1), "Bad empty string")
|
||||
/// # }
|
||||
/// # }
|
||||
///
|
||||
/// #[no_mangle]
|
||||
/// pub extern "C" fn mylib_print_string(
|
||||
/// // Strings come in as an `FfiStr`, which is a wrapper around a null terminated C string.
|
||||
/// thing_to_print: FfiStr<'_>,
|
||||
/// // Note that taking `&mut T` and `&T` is both allowed and encouraged, so long as `T: Sized`,
|
||||
/// // (e.g. it can't be a trait object, `&[T]`, a `&str`, etc). Also note that `Option<&T>` and
|
||||
/// // `Option<&mut T>` are also allowed, if you expect the caller to sometimes pass in null, but
|
||||
/// // that's the only case when it's currently to use `Option` in an argument list like this).
|
||||
/// error: &mut ExternError
|
||||
/// ) {
|
||||
/// // You should try to to do as little as possible outside the call_with_result,
|
||||
/// // to avoid a case where a panic occurs.
|
||||
/// ffi_support::call_with_result(error, || {
|
||||
/// let s = thing_to_print.as_str();
|
||||
/// if s.is_empty() {
|
||||
/// // This is a silly example!
|
||||
/// return Err(BadEmptyString);
|
||||
/// }
|
||||
/// println!("{}", s);
|
||||
/// Ok(())
|
||||
/// })
|
||||
/// }
|
||||
/// ```
|
||||
pub fn call_with_result<R, E, F>(out_error: &mut ExternError, callback: F) -> R::Value
|
||||
where
|
||||
F: panic::UnwindSafe + FnOnce() -> Result<R, E>,
|
||||
E: Into<ExternError>,
|
||||
R: IntoFfi,
|
||||
{
|
||||
call_with_result_impl(out_error, callback)
|
||||
}
|
||||
|
||||
/// Call a callback that returns a `T` while:
|
||||
///
|
||||
/// - Catching panics, and reporting them to C via [`ExternError`]
|
||||
/// - Converting `T` to a C-compatible type using [`IntoFfi`]
|
||||
///
|
||||
/// Note that you still need to provide an [`ExternError`] to this function, to report panics.
|
||||
///
|
||||
/// See [`call_with_result`] if you'd like to return a `Result<T, E>` (Note: `E` must
|
||||
/// be convertible to [`ExternError`]).
|
||||
///
|
||||
/// This (or [`call_with_result`]) should be in the majority of the FFI functions, see
|
||||
/// the crate top-level docs for more info.
|
||||
pub fn call_with_output<R, F>(out_error: &mut ExternError, callback: F) -> R::Value
|
||||
where
|
||||
F: panic::UnwindSafe + FnOnce() -> R,
|
||||
R: IntoFfi,
|
||||
{
|
||||
// We need something that's `Into<ExternError>`, even though we never return it, so just use
|
||||
// `ExternError` itself.
|
||||
call_with_result(out_error, || -> Result<_, ExternError> { Ok(callback()) })
|
||||
}
|
||||
|
||||
fn call_with_result_impl<R, E, F>(out_error: &mut ExternError, callback: F) -> R::Value
|
||||
where
|
||||
F: panic::UnwindSafe + FnOnce() -> Result<R, E>,
|
||||
E: Into<ExternError>,
|
||||
R: IntoFfi,
|
||||
{
|
||||
*out_error = ExternError::success();
|
||||
let res: thread::Result<(ExternError, R::Value)> = panic::catch_unwind(|| {
|
||||
init_panic_handling_once();
|
||||
match callback() {
|
||||
Ok(v) => (ExternError::default(), v.into_ffi_value()),
|
||||
Err(e) => (e.into(), R::ffi_default()),
|
||||
}
|
||||
});
|
||||
match res {
|
||||
Ok((err, o)) => {
|
||||
*out_error = err;
|
||||
o
|
||||
}
|
||||
Err(e) => {
|
||||
*out_error = e.into();
|
||||
R::ffi_default()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// This module exists just to expose a variant of [`call_with_result`] and [`call_with_output`]
|
||||
/// that aborts, instead of unwinding, on panic.
|
||||
pub mod abort_on_panic {
|
||||
use super::*;
|
||||
|
||||
// Struct that exists to automatically process::abort if we don't call
|
||||
// `std::mem::forget()` on it. This can have substantial performance
|
||||
// benefits over calling `std::panic::catch_unwind` and aborting if a panic
|
||||
// was caught, in addition to not requiring AssertUnwindSafe (for example).
|
||||
struct AbortOnDrop;
|
||||
impl Drop for AbortOnDrop {
|
||||
fn drop(&mut self) {
|
||||
std::process::abort();
|
||||
}
|
||||
}
|
||||
|
||||
/// A helper function useful for cases where you'd like to abort on panic,
|
||||
/// but aren't in a position where you'd like to return an FFI-compatible
|
||||
/// type.
|
||||
#[inline]
|
||||
pub fn with_abort_on_panic<R, F>(callback: F) -> R
|
||||
where
|
||||
F: FnOnce() -> R,
|
||||
{
|
||||
let aborter = AbortOnDrop;
|
||||
let res = callback();
|
||||
std::mem::forget(aborter);
|
||||
res
|
||||
}
|
||||
|
||||
/// Same as the root `call_with_result`, but aborts on panic instead of unwinding. See the
|
||||
/// `call_with_result` documentation for more.
|
||||
pub fn call_with_result<R, E, F>(out_error: &mut ExternError, callback: F) -> R::Value
|
||||
where
|
||||
F: FnOnce() -> Result<R, E>,
|
||||
E: Into<ExternError>,
|
||||
R: IntoFfi,
|
||||
{
|
||||
with_abort_on_panic(|| match callback() {
|
||||
Ok(v) => {
|
||||
*out_error = ExternError::default();
|
||||
v.into_ffi_value()
|
||||
}
|
||||
Err(e) => {
|
||||
*out_error = e.into();
|
||||
R::ffi_default()
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
/// Same as the root `call_with_output`, but aborts on panic instead of unwinding. As a result,
|
||||
/// it doesn't require a [`ExternError`] out argument. See the `call_with_output` documentation
|
||||
/// for more info.
|
||||
pub fn call_with_output<R, F>(callback: F) -> R::Value
|
||||
where
|
||||
F: FnOnce() -> R,
|
||||
R: IntoFfi,
|
||||
{
|
||||
with_abort_on_panic(callback).into_ffi_value()
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(feature = "log_panics")]
|
||||
fn init_panic_handling_once() {
|
||||
use std::sync::Once;
|
||||
static INIT_BACKTRACES: Once = Once::new();
|
||||
INIT_BACKTRACES.call_once(move || {
|
||||
#[cfg(all(feature = "log_backtraces", not(target_os = "android")))]
|
||||
{
|
||||
std::env::set_var("RUST_BACKTRACE", "1");
|
||||
}
|
||||
// Turn on a panic hook which logs both backtraces and the panic
|
||||
// "Location" (file/line). We do both in case we've been stripped,
|
||||
// ).
|
||||
std::panic::set_hook(Box::new(move |panic_info| {
|
||||
let (file, line) = if let Some(loc) = panic_info.location() {
|
||||
(loc.file(), loc.line())
|
||||
} else {
|
||||
// Apparently this won't happen but rust has reserved the
|
||||
// ability to start returning None from location in some cases
|
||||
// in the future.
|
||||
("<unknown>", 0)
|
||||
};
|
||||
log::error!("### Rust `panic!` hit at file '{}', line {}", file, line);
|
||||
#[cfg(all(feature = "log_backtraces", not(target_os = "android")))]
|
||||
{
|
||||
log::error!(" Complete stack trace:\n{:?}", backtrace::Backtrace::new());
|
||||
}
|
||||
}));
|
||||
});
|
||||
}
|
||||
|
||||
#[cfg(not(feature = "log_panics"))]
|
||||
fn init_panic_handling_once() {}
|
||||
|
||||
/// ByteBuffer is a struct that represents an array of bytes to be sent over the FFI boundaries.
|
||||
/// There are several cases when you might want to use this, but the primary one for us
|
||||
/// is for returning protobuf-encoded data to Swift and Java. The type is currently rather
|
||||
/// limited (implementing almost no functionality), however in the future it may be
|
||||
/// more expanded.
|
||||
///
|
||||
/// ## Caveats
|
||||
///
|
||||
/// Note that the order of the fields is `len` (an i64) then `data` (a `*mut u8`), getting
|
||||
/// this wrong on the other side of the FFI will cause memory corruption and crashes.
|
||||
/// `i64` is used for the length instead of `u64` and `usize` because JNA has interop
|
||||
/// issues with both these types.
|
||||
///
|
||||
/// ### `Drop` is not implemented
|
||||
///
|
||||
/// ByteBuffer does not implement Drop. This is intentional. Memory passed into it will
|
||||
/// be leaked if it is not explicitly destroyed by calling [`ByteBuffer::destroy`], or
|
||||
/// [`ByteBuffer::destroy_into_vec`]. This is for two reasons:
|
||||
///
|
||||
/// 1. In the future, we may allow it to be used for data that is not managed by
|
||||
/// the Rust allocator\*, and `ByteBuffer` assuming it's okay to automatically
|
||||
/// deallocate this data with the Rust allocator.
|
||||
///
|
||||
/// 2. Automatically running destructors in unsafe code is a
|
||||
/// [frequent footgun](https://without.boats/blog/two-memory-bugs-from-ringbahn/)
|
||||
/// (among many similar issues across many crates).
|
||||
///
|
||||
/// Note that calling `destroy` manually is often not needed, as usually you should
|
||||
/// be passing these to the function defined by [`define_bytebuffer_destructor!`] from
|
||||
/// the other side of the FFI.
|
||||
///
|
||||
/// Because this type is essentially *only* useful in unsafe or FFI code (and because
|
||||
/// the most common usage pattern does not require manually managing the memory), it
|
||||
/// does not implement `Drop`.
|
||||
///
|
||||
/// \* Note: in the case of multiple Rust shared libraries loaded at the same time,
|
||||
/// there may be multiple instances of "the Rust allocator" (one per shared library),
|
||||
/// in which case we're referring to whichever instance is active for the code using
|
||||
/// the `ByteBuffer`. Note that this doesn't occur on all platforms or build
|
||||
/// configurations, but treating allocators in different shared libraries as fully
|
||||
/// independent is always safe.
|
||||
///
|
||||
/// ## Layout/fields
|
||||
///
|
||||
/// This struct's field are not `pub` (mostly so that we can soundly implement `Send`, but also so
|
||||
/// that we can verify rust users are constructing them appropriately), the fields, their types, and
|
||||
/// their order are *very much* a part of the public API of this type. Consumers on the other side
|
||||
/// of the FFI will need to know its layout.
|
||||
///
|
||||
/// If this were a C struct, it would look like
|
||||
///
|
||||
/// ```c,no_run
|
||||
/// struct ByteBuffer {
|
||||
/// // Note: This should never be negative, but values above
|
||||
/// // INT64_MAX / i64::MAX are not allowed.
|
||||
/// int64_t len;
|
||||
/// // Note: nullable!
|
||||
/// uint8_t *data;
|
||||
/// };
|
||||
/// ```
|
||||
///
|
||||
/// In rust, there are two fields, in this order: `len: i64`, and `data: *mut u8`.
|
||||
///
|
||||
/// For clarity, the fact that the data pointer is nullable means that `Option<ByteBuffer>` is not
|
||||
/// the same size as ByteBuffer, and additionally is not FFI-safe (the latter point is not
|
||||
/// currently guaranteed anyway as of the time of writing this comment).
|
||||
///
|
||||
/// ### Description of fields
|
||||
///
|
||||
/// `data` is a pointer to an array of `len` bytes. Note that data can be a null pointer and therefore
|
||||
/// should be checked.
|
||||
///
|
||||
/// The bytes array is allocated on the heap and must be freed on it as well. Critically, if there
|
||||
/// are multiple rust shared libraries using being used in the same application, it *must be freed
|
||||
/// on the same heap that allocated it*, or you will corrupt both heaps.
|
||||
///
|
||||
/// Typically, this object is managed on the other side of the FFI (on the "FFI consumer"), which
|
||||
/// means you must expose a function to release the resources of `data` which can be done easily
|
||||
/// using the [`define_bytebuffer_destructor!`] macro provided by this crate.
|
||||
#[repr(C)]
|
||||
pub struct ByteBuffer {
|
||||
len: i64,
|
||||
data: *mut u8,
|
||||
}
|
||||
|
||||
impl From<Vec<u8>> for ByteBuffer {
|
||||
#[inline]
|
||||
fn from(bytes: Vec<u8>) -> Self {
|
||||
Self::from_vec(bytes)
|
||||
}
|
||||
}
|
||||
|
||||
impl ByteBuffer {
|
||||
/// Creates a `ByteBuffer` of the requested size, zero-filled.
|
||||
///
|
||||
/// The contents of the vector will not be dropped. Instead, `destroy` must
|
||||
/// be called later to reclaim this memory or it will be leaked.
|
||||
///
|
||||
/// ## Caveats
|
||||
///
|
||||
/// This will panic if the buffer length (`usize`) cannot fit into a `i64`.
|
||||
#[inline]
|
||||
pub fn new_with_size(size: usize) -> Self {
|
||||
// Note: `Vec` requires this internally on 64 bit platforms (and has a
|
||||
// stricter requirement on 32 bit ones), so this is just to be explicit.
|
||||
assert!(size < i64::MAX as usize);
|
||||
let mut buf = vec![];
|
||||
buf.reserve_exact(size);
|
||||
buf.resize(size, 0);
|
||||
ByteBuffer::from_vec(buf)
|
||||
}
|
||||
|
||||
/// Creates a `ByteBuffer` instance from a `Vec` instance.
|
||||
///
|
||||
/// The contents of the vector will not be dropped. Instead, `destroy` must
|
||||
/// be called later to reclaim this memory or it will be leaked.
|
||||
///
|
||||
/// ## Caveats
|
||||
///
|
||||
/// This will panic if the buffer length (`usize`) cannot fit into a `i64`.
|
||||
#[inline]
|
||||
pub fn from_vec(bytes: Vec<u8>) -> Self {
|
||||
use std::convert::TryFrom;
|
||||
let mut buf = bytes.into_boxed_slice();
|
||||
let data = buf.as_mut_ptr();
|
||||
let len = i64::try_from(buf.len()).expect("buffer length cannot fit into a i64.");
|
||||
std::mem::forget(buf);
|
||||
Self { len, data }
|
||||
}
|
||||
|
||||
/// View the data inside this `ByteBuffer` as a `&[u8]`.
|
||||
// TODO: Is it worth implementing `Deref`? Patches welcome if you need this.
|
||||
#[inline]
|
||||
pub fn as_slice(&self) -> &[u8] {
|
||||
if self.data.is_null() {
|
||||
&[]
|
||||
} else {
|
||||
unsafe { std::slice::from_raw_parts(self.data, self.len()) }
|
||||
}
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn len(&self) -> usize {
|
||||
use std::convert::TryInto;
|
||||
self.len
|
||||
.try_into()
|
||||
.expect("ByteBuffer length negative or overflowed")
|
||||
}
|
||||
|
||||
/// View the data inside this `ByteBuffer` as a `&mut [u8]`.
|
||||
// TODO: Is it worth implementing `DerefMut`? Patches welcome if you need this.
|
||||
#[inline]
|
||||
pub fn as_mut_slice(&mut self) -> &mut [u8] {
|
||||
if self.data.is_null() {
|
||||
&mut []
|
||||
} else {
|
||||
unsafe { std::slice::from_raw_parts_mut(self.data, self.len()) }
|
||||
}
|
||||
}
|
||||
|
||||
/// Deprecated alias for [`ByteBuffer::destroy_into_vec`].
|
||||
#[inline]
|
||||
#[deprecated = "Name is confusing, please use `destroy_into_vec` instead"]
|
||||
pub fn into_vec(self) -> Vec<u8> {
|
||||
self.destroy_into_vec()
|
||||
}
|
||||
|
||||
/// Convert this `ByteBuffer` into a Vec<u8>, taking ownership of the
|
||||
/// underlying memory, which will be freed using the rust allocator once the
|
||||
/// `Vec<u8>`'s lifetime is done.
|
||||
///
|
||||
/// If this is undesirable, you can do `bb.as_slice().to_vec()` to get a
|
||||
/// `Vec<u8>` containing a copy of this `ByteBuffer`'s underlying data.
|
||||
///
|
||||
/// ## Caveats
|
||||
///
|
||||
/// This is safe so long as the buffer is empty, or the data was allocated
|
||||
/// by Rust code, e.g. this is a ByteBuffer created by
|
||||
/// `ByteBuffer::from_vec` or `Default::default`.
|
||||
///
|
||||
/// If the ByteBuffer were allocated by something other than the
|
||||
/// current/local Rust `global_allocator`, then calling `destroy` is
|
||||
/// fundamentally broken.
|
||||
///
|
||||
/// For example, if it were allocated externally by some other language's
|
||||
/// runtime, or if it were allocated by the global allocator of some other
|
||||
/// Rust shared object in the same application, the behavior is undefined
|
||||
/// (and likely to cause problems).
|
||||
///
|
||||
/// Note that this currently can only happen if the `ByteBuffer` is passed
|
||||
/// to you via an `extern "C"` function that you expose, as opposed to being
|
||||
/// created locally.
|
||||
#[inline]
|
||||
pub fn destroy_into_vec(self) -> Vec<u8> {
|
||||
if self.data.is_null() {
|
||||
vec![]
|
||||
} else {
|
||||
let len = self.len();
|
||||
// Safety: This is correct because we convert to a Box<[u8]> first,
|
||||
// which is a design constraint of RawVec.
|
||||
unsafe { Vec::from_raw_parts(self.data, len, len) }
|
||||
}
|
||||
}
|
||||
|
||||
/// Reclaim memory stored in this ByteBuffer.
|
||||
///
|
||||
/// You typically should not call this manually, and instead expose a
|
||||
/// function that does so via [`define_bytebuffer_destructor!`].
|
||||
///
|
||||
/// ## Caveats
|
||||
///
|
||||
/// This is safe so long as the buffer is empty, or the data was allocated
|
||||
/// by Rust code, e.g. this is a ByteBuffer created by
|
||||
/// `ByteBuffer::from_vec` or `Default::default`.
|
||||
///
|
||||
/// If the ByteBuffer were allocated by something other than the
|
||||
/// current/local Rust `global_allocator`, then calling `destroy` is
|
||||
/// fundamentally broken.
|
||||
///
|
||||
/// For example, if it were allocated externally by some other language's
|
||||
/// runtime, or if it were allocated by the global allocator of some other
|
||||
/// Rust shared object in the same application, the behavior is undefined
|
||||
/// (and likely to cause problems).
|
||||
///
|
||||
/// Note that this currently can only happen if the `ByteBuffer` is passed
|
||||
/// to you via an `extern "C"` function that you expose, as opposed to being
|
||||
/// created locally.
|
||||
#[inline]
|
||||
pub fn destroy(self) {
|
||||
// Note: the drop is just for clarity, of course.
|
||||
drop(self.destroy_into_vec())
|
||||
}
|
||||
}
|
||||
|
||||
impl Default for ByteBuffer {
|
||||
#[inline]
|
||||
fn default() -> Self {
|
||||
Self {
|
||||
len: 0_i64,
|
||||
data: std::ptr::null_mut(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod test {
|
||||
use super::*;
|
||||
#[test]
|
||||
fn test_bb_access() {
|
||||
let mut bb = ByteBuffer::from(vec![1u8, 2, 3]);
|
||||
assert_eq!(bb.as_slice(), &[1u8, 2, 3]);
|
||||
assert_eq!(bb.as_mut_slice(), &mut [1u8, 2, 3]);
|
||||
bb.as_mut_slice()[2] = 4;
|
||||
|
||||
// Use into_vec to cover both into_vec and destroy_into_vec.
|
||||
#[allow(deprecated)]
|
||||
{
|
||||
assert_eq!(bb.into_vec(), &[1u8, 2, 4]);
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_bb_empty() {
|
||||
let mut bb = ByteBuffer::default();
|
||||
assert_eq!(bb.as_slice(), &[]);
|
||||
assert_eq!(bb.as_mut_slice(), &[]);
|
||||
assert_eq!(bb.destroy_into_vec(), &[]);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_bb_new() {
|
||||
let bb = ByteBuffer::new_with_size(5);
|
||||
assert_eq!(bb.as_slice(), &[0u8, 0, 0, 0, 0]);
|
||||
bb.destroy();
|
||||
|
||||
let bb = ByteBuffer::new_with_size(0);
|
||||
assert_eq!(bb.as_slice(), &[]);
|
||||
assert!(!bb.data.is_null());
|
||||
bb.destroy();
|
||||
|
||||
let bb = ByteBuffer::from_vec(vec![]);
|
||||
assert_eq!(bb.as_slice(), &[]);
|
||||
assert!(!bb.data.is_null());
|
||||
bb.destroy();
|
||||
}
|
||||
}
|
|
@ -1,358 +0,0 @@
|
|||
/* Copyright 2018-2019 Mozilla Foundation
|
||||
*
|
||||
* Licensed under the Apache License (Version 2.0), or the MIT license,
|
||||
* (the "Licenses") at your option. You may not use this file except in
|
||||
* compliance with one of the Licenses. You may obtain copies of the
|
||||
* Licenses at:
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
* http://opensource.org/licenses/MIT
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the Licenses is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the Licenses for the specific language governing permissions and
|
||||
* limitations under the Licenses. */
|
||||
|
||||
/// Implements [`IntoFfi`] for the provided types (more than one may be passed in) by allocating
|
||||
/// `$T` on the heap as an opaque pointer.
|
||||
///
|
||||
/// This is typically going to be used from the "Rust component", and not the "FFI component" (see
|
||||
/// the top level crate documentation for more information), however you will still need to
|
||||
/// implement a destructor in the FFI component using [`define_box_destructor!`].
|
||||
///
|
||||
/// In general, is only safe to do for `send` types (even this is dodgy, but it's often necessary
|
||||
/// to keep the locking on the other side of the FFI, so Sync is too harsh), so we enforce this in
|
||||
/// this macro. (You're still free to implement this manually, if this restriction is too harsh
|
||||
/// for your use case and you're certain you know what you're doing).
|
||||
#[macro_export]
|
||||
macro_rules! implement_into_ffi_by_pointer {
|
||||
($($T:ty),* $(,)*) => {$(
|
||||
unsafe impl $crate::IntoFfi for $T where $T: Send {
|
||||
type Value = *mut $T;
|
||||
|
||||
#[inline]
|
||||
fn ffi_default() -> *mut $T {
|
||||
std::ptr::null_mut()
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn into_ffi_value(self) -> *mut $T {
|
||||
Box::into_raw(Box::new(self))
|
||||
}
|
||||
}
|
||||
)*}
|
||||
}
|
||||
|
||||
/// Implements [`IntoFfi`] for the provided types (more than one may be passed
|
||||
/// in) by converting to the type to a JSON string.
|
||||
///
|
||||
/// Additionally, most of the time we recomment using this crate's protobuf
|
||||
/// support, instead of JSON.
|
||||
///
|
||||
/// This is typically going to be used from the "Rust component", and not the
|
||||
/// "FFI component" (see the top level crate documentation for more
|
||||
/// information).
|
||||
///
|
||||
/// Note: Each type passed in must implement or derive `serde::Serialize`.
|
||||
///
|
||||
/// Note: for this to works, the crate it's called in must depend on `serde` and
|
||||
/// `serde_json`.
|
||||
///
|
||||
/// ## Panics
|
||||
///
|
||||
/// The [`IntoFfi`] implementation this macro generates may panic in the
|
||||
/// following cases:
|
||||
///
|
||||
/// - You've passed a type that contains a Map that has non-string keys (which
|
||||
/// can't be represented in JSON).
|
||||
///
|
||||
/// - You've passed a type which has a custom serializer, and the custom
|
||||
/// serializer failed.
|
||||
///
|
||||
/// These cases are both rare enough that this still seems fine for the majority
|
||||
/// of uses.
|
||||
#[macro_export]
|
||||
macro_rules! implement_into_ffi_by_json {
|
||||
($($T:ty),* $(,)*) => {$(
|
||||
unsafe impl $crate::IntoFfi for $T where $T: serde::Serialize {
|
||||
type Value = *mut std::os::raw::c_char;
|
||||
#[inline]
|
||||
fn ffi_default() -> *mut std::os::raw::c_char {
|
||||
std::ptr::null_mut()
|
||||
}
|
||||
#[inline]
|
||||
fn into_ffi_value(self) -> *mut std::os::raw::c_char {
|
||||
// This panic is inside our catch_panic, so it should be fine.
|
||||
// We've also documented the case where the IntoFfi impl that
|
||||
// calls this panics, and it's rare enough that it shouldn't
|
||||
// matter that if it happens we return an ExternError
|
||||
// representing a panic instead of one of some other type
|
||||
// (especially given that the application isn't likely to be
|
||||
// able to meaningfully handle JSON serialization failure).
|
||||
let as_string = serde_json::to_string(&self).unwrap();
|
||||
$crate::rust_string_to_c(as_string)
|
||||
}
|
||||
}
|
||||
)*}
|
||||
}
|
||||
|
||||
/// Implements [`IntoFfi`] for the provided types (more than one may be passed in) implementing
|
||||
/// `prost::Message` (protobuf auto-generated type) by converting to the type to a [`ByteBuffer`].
|
||||
/// This [`ByteBuffer`] should later be passed by value.
|
||||
///
|
||||
/// Note: for this to works, the crate it's called in must depend on `prost`.
|
||||
///
|
||||
/// Note: Each type passed in must implement or derive `prost::Message`.
|
||||
#[macro_export]
|
||||
macro_rules! implement_into_ffi_by_protobuf {
|
||||
($($FFIType:ty),* $(,)*) => {$(
|
||||
unsafe impl $crate::IntoFfi for $FFIType where $FFIType: prost::Message {
|
||||
type Value = $crate::ByteBuffer;
|
||||
#[inline]
|
||||
fn ffi_default() -> Self::Value {
|
||||
Default::default()
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn into_ffi_value(self) -> Self::Value {
|
||||
use prost::Message;
|
||||
let mut bytes = Vec::with_capacity(self.encoded_len());
|
||||
// Unwrap is safe, since we have reserved sufficient capacity in
|
||||
// the vector.
|
||||
self.encode(&mut bytes).unwrap();
|
||||
bytes.into()
|
||||
}
|
||||
}
|
||||
)*}
|
||||
}
|
||||
|
||||
/// Implement IntoFfi for a type by converting through another type.
|
||||
///
|
||||
/// The argument `$MidTy` argument must implement `From<$SrcTy>` and
|
||||
/// [`InfoFfi`].
|
||||
///
|
||||
/// This is provided (even though it's trivial) because it is always safe (well,
|
||||
/// so long as `$MidTy`'s [`IntoFfi`] implementation is correct), but would
|
||||
/// otherwise require use of `unsafe` to implement.
|
||||
#[macro_export]
|
||||
macro_rules! implement_into_ffi_by_delegation {
|
||||
($SrcTy:ty, $MidTy:ty) => {
|
||||
unsafe impl $crate::IntoFfi for $SrcTy
|
||||
where
|
||||
$MidTy: From<$SrcTy> + $crate::IntoFfi,
|
||||
{
|
||||
// The <$MidTy as SomeTrait>::method is required even when it would
|
||||
// be ambiguous due to some obscure details of macro syntax.
|
||||
type Value = <$MidTy as $crate::IntoFfi>::Value;
|
||||
|
||||
#[inline]
|
||||
fn ffi_default() -> Self::Value {
|
||||
<$MidTy as $crate::IntoFfi>::ffi_default()
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn into_ffi_value(self) -> Self::Value {
|
||||
use $crate::IntoFfi;
|
||||
<$MidTy as From<$SrcTy>>::from(self).into_ffi_value()
|
||||
}
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
/// For a number of reasons (name collisions are a big one, but, it also wouldn't work on all
|
||||
/// platforms), we cannot export `extern "C"` functions from this library. However, it's pretty
|
||||
/// common to want to free strings allocated by rust, so many libraries will need this, so we
|
||||
/// provide it as a macro.
|
||||
///
|
||||
/// It simply expands to a `#[no_mangle] pub unsafe extern "C" fn` which wraps this crate's
|
||||
/// [`destroy_c_string`] function.
|
||||
///
|
||||
/// ## Caveats
|
||||
///
|
||||
/// If you're using multiple separately compiled rust libraries in your application, it's critical
|
||||
/// that you are careful to only ever free strings allocated by a Rust library using the same rust
|
||||
/// library. Passing them to a different Rust library's string destructor will cause you to corrupt
|
||||
/// multiple heaps.
|
||||
///
|
||||
/// Additionally, be sure that all strings you pass to this were actually allocated by rust. It's a
|
||||
/// common issue for JNA code to transparently convert Pointers to things to Strings behind the
|
||||
/// scenes, which is quite risky here. (To avoid this in JNA, only use `String` for passing
|
||||
/// read-only strings into Rust, e.g. it's for passing `*const c_char`. All other uses should use
|
||||
/// `Pointer` and `getString()`).
|
||||
///
|
||||
/// Finally, to avoid name collisions, it is strongly recommended that you provide an name for this
|
||||
/// function unique to your library.
|
||||
///
|
||||
/// ## Example
|
||||
///
|
||||
/// ```rust
|
||||
/// # use ffi_support::define_string_destructor;
|
||||
/// define_string_destructor!(mylib_destroy_string);
|
||||
/// ```
|
||||
#[macro_export]
|
||||
macro_rules! define_string_destructor {
|
||||
($mylib_destroy_string:ident) => {
|
||||
/// Public destructor for strings managed by the other side of the FFI.
|
||||
///
|
||||
/// # Safety
|
||||
///
|
||||
/// This will free the string pointer it gets passed in as an argument,
|
||||
/// and thus can be wildly unsafe if misused.
|
||||
///
|
||||
/// See the documentation of `ffi_support::destroy_c_string` and
|
||||
/// `ffi_support::define_string_destructor!` for further info.
|
||||
#[no_mangle]
|
||||
pub unsafe extern "C" fn $mylib_destroy_string(s: *mut std::os::raw::c_char) {
|
||||
// Note: This should never happen, but in the case of a bug aborting
|
||||
// here is better than the badness that happens if we unwind across
|
||||
// the FFI boundary.
|
||||
$crate::abort_on_panic::with_abort_on_panic(|| {
|
||||
if !s.is_null() {
|
||||
$crate::destroy_c_string(s)
|
||||
}
|
||||
});
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
/// Define a (public) destructor for a type that was allocated by
|
||||
/// `Box::into_raw(Box::new(value))` (e.g. a pointer which is probably opaque).
|
||||
///
|
||||
/// ## Caveats
|
||||
///
|
||||
/// When called over the FFI, this can go wrong in a ridiculous number of ways,
|
||||
/// and we can't really prevent any of them. But essentially, the caller (on the
|
||||
/// other side of the FFI) needs to be extremely careful to ensure that it stops
|
||||
/// using the pointer after it's freed.
|
||||
///
|
||||
/// Also, to avoid name collisions, it is strongly recommended that you provide
|
||||
/// an name for this function unique to your library. (This is true for all
|
||||
/// functions you expose).
|
||||
///
|
||||
/// However, when called from rust, this is safe, as it becomes a function that
|
||||
/// just drops a `Option<Box<T>>` with some panic handling.
|
||||
///
|
||||
/// ## Example
|
||||
///
|
||||
/// ```rust
|
||||
/// # use ffi_support::define_box_destructor;
|
||||
/// struct CoolType(Vec<i32>);
|
||||
///
|
||||
/// define_box_destructor!(CoolType, mylib_destroy_cooltype);
|
||||
/// ```
|
||||
#[macro_export]
|
||||
macro_rules! define_box_destructor {
|
||||
($T:ty, $destructor_name:ident) => {
|
||||
/// # Safety
|
||||
/// This is equivalent to calling Box::from_raw with panic handling, and
|
||||
/// thus inherits [`Box::from_raw`]'s safety properties. That is to say,
|
||||
/// this function is wildly unsafe.
|
||||
#[no_mangle]
|
||||
pub unsafe extern "C" fn $destructor_name(v: *mut $T) {
|
||||
// We should consider passing an error parameter in here rather than
|
||||
// aborting, but at the moment the only case where we do this
|
||||
// (interrupt handles) should never panic in Drop, so it's probably
|
||||
// fine.
|
||||
$crate::abort_on_panic::with_abort_on_panic(|| {
|
||||
if !v.is_null() {
|
||||
drop(Box::from_raw(v))
|
||||
}
|
||||
});
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
/// Define a (public) destructor for the ByteBuffer type.
|
||||
///
|
||||
/// ## Caveats
|
||||
///
|
||||
/// If you're using multiple separately compiled rust libraries in your application, it's critical
|
||||
/// that you are careful to only ever free `ByteBuffer` instances allocated by a Rust library using
|
||||
/// the same rust library. Passing them to a different Rust library's string destructor will cause
|
||||
/// you to corrupt multiple heaps.
|
||||
/// One common ByteBuffer destructor is defined per Rust library.
|
||||
///
|
||||
/// Also, to avoid name collisions, it is strongly recommended that you provide an name for this
|
||||
/// function unique to your library. (This is true for all functions you expose).
|
||||
///
|
||||
/// ## Example
|
||||
///
|
||||
/// ```rust
|
||||
/// # use ffi_support::define_bytebuffer_destructor;
|
||||
/// define_bytebuffer_destructor!(mylib_destroy_bytebuffer);
|
||||
/// ```
|
||||
#[macro_export]
|
||||
macro_rules! define_bytebuffer_destructor {
|
||||
($destructor_name:ident) => {
|
||||
#[no_mangle]
|
||||
pub extern "C" fn $destructor_name(v: $crate::ByteBuffer) {
|
||||
// Note: This should never happen, but in the case of a bug aborting
|
||||
// here is better than the badness that happens if we unwind across
|
||||
// the FFI boundary.
|
||||
$crate::abort_on_panic::with_abort_on_panic(|| v.destroy())
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
/// Define a (public) destructor for a type that lives inside a lazy_static
|
||||
/// [`ConcurrentHandleMap`].
|
||||
///
|
||||
/// Note that this is actually totally safe, unlike the other
|
||||
/// `define_blah_destructor` macros.
|
||||
///
|
||||
/// A critical difference, however, is that this dtor takes an `err` out
|
||||
/// parameter to indicate failure. This difference is why the name is different
|
||||
/// as well (deleter vs destructor).
|
||||
///
|
||||
/// ## Example
|
||||
///
|
||||
/// ```rust
|
||||
/// # use lazy_static::lazy_static;
|
||||
/// # use ffi_support::{ConcurrentHandleMap, define_handle_map_deleter};
|
||||
/// struct Thing(Vec<i32>);
|
||||
/// // Somewhere...
|
||||
/// lazy_static! {
|
||||
/// static ref THING_HANDLES: ConcurrentHandleMap<Thing> = ConcurrentHandleMap::new();
|
||||
/// }
|
||||
/// define_handle_map_deleter!(THING_HANDLES, mylib_destroy_thing);
|
||||
/// ```
|
||||
#[macro_export]
|
||||
macro_rules! define_handle_map_deleter {
|
||||
($HANDLE_MAP_NAME:ident, $destructor_name:ident) => {
|
||||
#[no_mangle]
|
||||
pub extern "C" fn $destructor_name(v: u64, err: &mut $crate::ExternError) {
|
||||
$crate::call_with_result(err, || {
|
||||
// Force type errors here.
|
||||
let map: &$crate::ConcurrentHandleMap<_> = &*$HANDLE_MAP_NAME;
|
||||
map.delete_u64(v)
|
||||
})
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
/// Force a compile error if the condition is not met. Requires a unique name
|
||||
/// for the assertion for... reasons. This is included mainly because it's a
|
||||
/// common desire for FFI code, but not for other sorts of code.
|
||||
///
|
||||
/// # Examples
|
||||
///
|
||||
/// Failing example:
|
||||
///
|
||||
/// ```compile_fail
|
||||
/// ffi_support::static_assert!(THIS_SHOULD_FAIL, false);
|
||||
/// ```
|
||||
///
|
||||
/// Passing example:
|
||||
///
|
||||
/// ```
|
||||
/// ffi_support::static_assert!(THIS_SHOULD_PASS, true);
|
||||
/// ```
|
||||
#[macro_export]
|
||||
macro_rules! static_assert {
|
||||
($ASSERT_NAME:ident, $test:expr) => {
|
||||
#[allow(dead_code, nonstandard_style)]
|
||||
const $ASSERT_NAME: [u8; 0 - (!$test as bool as usize)] =
|
||||
[0u8; 0 - (!$test as bool as usize)];
|
||||
};
|
||||
}
|
|
@ -1,162 +0,0 @@
|
|||
/* Copyright 2018-2019 Mozilla Foundation
|
||||
*
|
||||
* Licensed under the Apache License (Version 2.0), or the MIT license,
|
||||
* (the "Licenses") at your option. You may not use this file except in
|
||||
* compliance with one of the Licenses. You may obtain copies of the
|
||||
* Licenses at:
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
* http://opensource.org/licenses/MIT
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the Licenses is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the Licenses for the specific language governing permissions and
|
||||
* limitations under the Licenses. */
|
||||
|
||||
use crate::FfiStr;
|
||||
use std::ffi::CString;
|
||||
use std::os::raw::c_char;
|
||||
use std::ptr;
|
||||
|
||||
/// Convert a rust string into a NUL-terminated utf-8 string suitable for passing to C, or to things
|
||||
/// ABI-compatible with C.
|
||||
///
|
||||
/// Important: This string must eventually be freed. You may either do that using the
|
||||
/// [`destroy_c_string`] method (or, if you must, by dropping the underlying [`std::ffi::CString`]
|
||||
/// after recovering it via [`std::ffi::CString::from_raw`]).
|
||||
///
|
||||
/// It's common to want to allow the consumer (e.g. on the "C" side of the FFI) to be allowed to
|
||||
/// free this memory, and the macro [`define_string_destructor!`] may be used to do so.
|
||||
///
|
||||
/// ## Panics
|
||||
///
|
||||
/// This function may panic if the argument has an interior null byte. This is fairly rare, but
|
||||
/// is possible in theory.
|
||||
#[inline]
|
||||
pub fn rust_string_to_c(rust_string: impl Into<String>) -> *mut c_char {
|
||||
CString::new(rust_string.into())
|
||||
.expect("Error: Rust string contained an interior null byte.")
|
||||
.into_raw()
|
||||
}
|
||||
|
||||
/// Variant of [`rust_string_to_c`] which takes an Option, and returns null for None.
|
||||
#[inline]
|
||||
pub fn opt_rust_string_to_c(opt_rust_string: Option<impl Into<String>>) -> *mut c_char {
|
||||
if let Some(s) = opt_rust_string {
|
||||
rust_string_to_c(s)
|
||||
} else {
|
||||
ptr::null_mut()
|
||||
}
|
||||
}
|
||||
|
||||
/// Free the memory of a string created by [`rust_string_to_c`] on the rust heap. If `c_string` is
|
||||
/// null, this is a no-op.
|
||||
///
|
||||
/// See the [`define_string_destructor!`] macro which may be used for exposing this function over
|
||||
/// the FFI.
|
||||
///
|
||||
/// ## Safety
|
||||
///
|
||||
/// This is inherently unsafe, since we're deallocating memory. Be sure
|
||||
///
|
||||
/// - Nobody can use the memory after it's deallocated.
|
||||
/// - The memory was actually allocated on this heap (and it's not a string from the other side of
|
||||
/// the FFI which was allocated on e.g. the C heap).
|
||||
/// - If multiple separate rust libraries are in use (for example, as DLLs) in a single program,
|
||||
/// you must also make sure that the rust library that allocated the memory is also the one
|
||||
/// that frees it.
|
||||
///
|
||||
/// See documentation for [`define_string_destructor!`], which gives a more complete overview of the
|
||||
/// potential issues.
|
||||
#[inline]
|
||||
pub unsafe fn destroy_c_string(cstring: *mut c_char) {
|
||||
// we're not guaranteed to be in a place where we can complain about this beyond logging,
|
||||
// and there's an obvious way to handle it.
|
||||
if !cstring.is_null() {
|
||||
drop(CString::from_raw(cstring))
|
||||
}
|
||||
}
|
||||
|
||||
/// Convert a null-terminated C string to a rust `str`. This does not take ownership of the string,
|
||||
/// and you should be careful about the lifetime of the resulting string. Note that strings
|
||||
/// containing invalid UTF-8 are replaced with the empty string (for many cases, you will want to
|
||||
/// use [`rust_string_from_c`] instead, which will do a lossy conversion).
|
||||
///
|
||||
/// If you actually need an owned rust `String`, you're encouraged to use [`rust_string_from_c`],
|
||||
/// which, as mentioned, also behaves better in the face of invalid UTF-8.
|
||||
///
|
||||
/// ## Safety
|
||||
///
|
||||
/// This is unsafe because we read from a raw pointer, which may or may not be valid.
|
||||
///
|
||||
/// We also assume `c_string` is a null terminated string, and have no way of knowing if that's
|
||||
/// actually true. If it's not, we'll read arbitrary memory from the heap until we see a '\0', which
|
||||
/// can result in a enormous number of problems.
|
||||
///
|
||||
/// ## Panics
|
||||
///
|
||||
/// Panics if it's argument is null, see [`opt_rust_str_from_c`] for a variant that returns None in
|
||||
/// this case instead.
|
||||
///
|
||||
/// Note: This means it's forbidden to call this outside of a `call_with_result` (or something else
|
||||
/// that uses [`std::panic::catch_unwind`]), as it is UB to panic across the FFI boundary.
|
||||
#[inline]
|
||||
#[deprecated(since = "0.3.0", note = "Please use FfiStr::as_str instead")]
|
||||
pub unsafe fn rust_str_from_c<'a>(c_string: *const c_char) -> &'a str {
|
||||
FfiStr::from_raw(c_string).as_str()
|
||||
}
|
||||
|
||||
/// Same as `rust_string_from_c`, but returns None if `c_string` is null instead of asserting.
|
||||
///
|
||||
/// ## Safety
|
||||
///
|
||||
/// This is unsafe because we read from a raw pointer, which may or may not be valid.
|
||||
///
|
||||
/// We also assume `c_string` is a null terminated string, and have no way of knowing if that's
|
||||
/// actually true. If it's not, we'll read arbitrary memory from the heap until we see a '\0', which
|
||||
/// can result in a enormous number of problems.
|
||||
#[inline]
|
||||
#[deprecated(since = "0.3.0", note = "Please use FfiStr::as_opt_str instead")]
|
||||
pub unsafe fn opt_rust_str_from_c<'a>(c_string: *const c_char) -> Option<&'a str> {
|
||||
FfiStr::from_raw(c_string).as_opt_str()
|
||||
}
|
||||
|
||||
/// Convert a null-terminated C into an owned rust string, replacing invalid UTF-8 with the
|
||||
/// unicode replacement character.
|
||||
///
|
||||
/// ## Safety
|
||||
///
|
||||
/// This is unsafe because we dereference a raw pointer, which may or may not be valid.
|
||||
///
|
||||
/// We also assume `c_string` is a null terminated string, and have no way of knowing if that's
|
||||
/// actually true. If it's not, we'll read arbitrary memory from the heap until we see a '\0', which
|
||||
/// can result in a enormous number of problems.
|
||||
///
|
||||
/// ## Panics
|
||||
///
|
||||
/// Panics if it's argument is null. See also [`opt_rust_string_from_c`], which returns None
|
||||
/// instead.
|
||||
///
|
||||
/// Note: This means it's forbidden to call this outside of a `call_with_result` (or something else
|
||||
/// that uses `std::panic::catch_unwind`), as it is UB to panic across the FFI boundary.
|
||||
#[inline]
|
||||
#[deprecated(since = "0.3.0", note = "Please use FfiStr::into_string instead")]
|
||||
pub unsafe fn rust_string_from_c(c_string: *const c_char) -> String {
|
||||
FfiStr::from_raw(c_string).into_string()
|
||||
}
|
||||
|
||||
/// Same as `rust_string_from_c`, but returns None if `c_string` is null instead of asserting.
|
||||
///
|
||||
/// ## Safety
|
||||
///
|
||||
/// This is unsafe because we dereference a raw pointer, which may or may not be valid.
|
||||
///
|
||||
/// We also assume `c_string` is a null terminated string, and have no way of knowing if that's
|
||||
/// actually true. If it's not, we'll read arbitrary memory from the heap until we see a '\0', which
|
||||
/// can result in a enormous number of problems.
|
||||
#[inline]
|
||||
#[deprecated(since = "0.3.0", note = "Please use FfiStr::into_opt_string instead")]
|
||||
pub unsafe fn opt_rust_string_from_c(c_string: *const c_char) -> Option<String> {
|
||||
FfiStr::from_raw(c_string).into_opt_string()
|
||||
}
|
|
@ -138,7 +138,7 @@ the details of which are reproduced below.
|
|||
</license>
|
||||
<license>
|
||||
<name>Apache License 2.0: ffi-support</name>
|
||||
<url>https://raw.githubusercontent.com/mozilla/application-services/main/components/support/ffi/LICENSE-APACHE</url>
|
||||
<url>https://raw.githubusercontent.com/mozilla/ffi-support/main/LICENSE-APACHE</url>
|
||||
</license>
|
||||
<license>
|
||||
<name>Apache License 2.0: form_urlencoded</name>
|
||||
|
|
|
@ -1,19 +0,0 @@
|
|||
[package]
|
||||
name = "ffi-support-extra-test"
|
||||
version = "0.1.0"
|
||||
authors = ["sync-team@mozilla.com"]
|
||||
license = "MPL-2.0"
|
||||
edition = "2018"
|
||||
publish = false
|
||||
|
||||
[[test]]
|
||||
path = "src/test.rs"
|
||||
name = "ffi-support-extra"
|
||||
|
||||
[dev-dependencies]
|
||||
ffi-support = { version = "0.4", path = "../../../components/support/ffi" }
|
||||
rayon = "1.3"
|
||||
env_logger = { version = "0.7", default-features = false }
|
||||
log = "0.4"
|
||||
rand = "0.7"
|
||||
|
|
@ -1,89 +0,0 @@
|
|||
/* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
||||
|
||||
//! This test is a stress test meant to trigger some bugs seen prior to the use
|
||||
//! of handlemaps. See /docs/design/test-faster.md for why it's split -- TLDR:
|
||||
//! it uses rayon and is hard to rewrite with normal threads.
|
||||
|
||||
use ffi_support::{ConcurrentHandleMap, ExternError};
|
||||
use std::sync::atomic::{AtomicUsize, Ordering};
|
||||
use std::sync::Arc;
|
||||
|
||||
fn with_error<F: FnOnce(&mut ExternError) -> T, T>(callback: F) -> T {
|
||||
let mut e = ExternError::success();
|
||||
let result = callback(&mut e);
|
||||
if let Some(m) = unsafe { e.get_and_consume_message() } {
|
||||
panic!("unexpected error: {}", m);
|
||||
}
|
||||
result
|
||||
}
|
||||
|
||||
struct DropChecking {
|
||||
counter: Arc<AtomicUsize>,
|
||||
id: usize,
|
||||
}
|
||||
impl Drop for DropChecking {
|
||||
fn drop(&mut self) {
|
||||
let val = self.counter.fetch_add(1, Ordering::SeqCst);
|
||||
log::debug!("Dropped {} :: {}", self.id, val);
|
||||
}
|
||||
}
|
||||
#[test]
|
||||
fn test_concurrent_drop() {
|
||||
use rand::prelude::*;
|
||||
use rayon::prelude::*;
|
||||
let _ = env_logger::try_init();
|
||||
let drop_counter = Arc::new(AtomicUsize::new(0));
|
||||
let id = Arc::new(AtomicUsize::new(1));
|
||||
let map = ConcurrentHandleMap::new();
|
||||
let count = 1000;
|
||||
let mut handles = (0..count)
|
||||
.into_par_iter()
|
||||
.map(|_| {
|
||||
let id = id.fetch_add(1, Ordering::SeqCst);
|
||||
let handle = with_error(|e| {
|
||||
map.insert_with_output(e, || {
|
||||
log::debug!("Created {}", id);
|
||||
DropChecking {
|
||||
counter: drop_counter.clone(),
|
||||
id,
|
||||
}
|
||||
})
|
||||
});
|
||||
(id, handle)
|
||||
})
|
||||
.collect::<Vec<_>>();
|
||||
|
||||
handles.shuffle(&mut thread_rng());
|
||||
|
||||
assert_eq!(drop_counter.load(Ordering::SeqCst), 0);
|
||||
handles.par_iter().for_each(|(id, h)| {
|
||||
with_error(|e| {
|
||||
map.call_with_output(e, *h, |val| {
|
||||
assert_eq!(val.id, *id);
|
||||
})
|
||||
});
|
||||
});
|
||||
|
||||
assert_eq!(drop_counter.load(Ordering::SeqCst), 0);
|
||||
|
||||
handles.par_iter().for_each(|(id, h)| {
|
||||
with_error(|e| {
|
||||
map.call_with_output(e, *h, |val| {
|
||||
assert_eq!(val.id, *id);
|
||||
})
|
||||
});
|
||||
});
|
||||
|
||||
handles.par_iter().for_each(|(id, h)| {
|
||||
let item = map
|
||||
.remove_u64(*h)
|
||||
.expect("remove to succeed")
|
||||
.expect("item to exist");
|
||||
assert_eq!(item.id, *id);
|
||||
let h = map.insert(item).into_u64();
|
||||
map.delete_u64(h).expect("delete to succeed");
|
||||
});
|
||||
assert_eq!(drop_counter.load(Ordering::SeqCst), count);
|
||||
}
|
|
@ -557,7 +557,7 @@ PACKAGE_METADATA_FIXUPS = {
|
|||
"ffi-support": {
|
||||
"license_url": {
|
||||
"check": None,
|
||||
"fixup": "https://raw.githubusercontent.com/mozilla/application-services/main/components/support/ffi/LICENSE-APACHE"
|
||||
"fixup": "https://raw.githubusercontent.com/mozilla/ffi-support/main/LICENSE-APACHE"
|
||||
},
|
||||
},
|
||||
"humantime": {
|
||||
|
|
Загрузка…
Ссылка в новой задаче