diff --git a/.github/workflows/build-test.yml b/.github/workflows/build-test.yml index b4bad6e49db..0e7be175fe8 100644 --- a/.github/workflows/build-test.yml +++ b/.github/workflows/build-test.yml @@ -112,10 +112,11 @@ jobs: run: rustup show - name: Load cortex target for no_std build. run: | - rustup install nightly - rustup component add --toolchain nightly rust-src - rustup target add thumbv7m-none-eabi --toolchain nightly - rustup target add thumbv8m.main-none-eabihf --toolchain nightly + rustup install nightly-2021-02-28 + rustup component add --toolchain nightly-2021-02-28 rust-src + rustup target add thumbv7m-none-eabi --toolchain nightly-2021-02-28 + rustup target add thumbv8m.main-none-eabihf --toolchain nightly-2021-02-28 + rustup target add x86_64-unknown-linux-gnu --toolchain nightly-2021-02-28 - name: Get cargo-make version id: cargo-make-version run: | diff --git a/Cargo.lock b/Cargo.lock index a843fde6b32..a2cbfb5c055 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -982,8 +982,10 @@ dependencies = [ "icu_decimal", "icu_locale_canonicalizer", "icu_locid", + "icu_locid_macros", "icu_plurals", "icu_provider", + "icu_provider_blob", "icu_provider_fs", "icu_testdata", "log", @@ -1160,6 +1162,7 @@ dependencies = [ "log", "postcard", "serde", + "writeable", ] [[package]] diff --git a/ffi/capi/Cargo.toml b/ffi/capi/Cargo.toml index 3f3e3b692c3..e1df53bbb79 100644 --- a/ffi/capi/Cargo.toml +++ b/ffi/capi/Cargo.toml @@ -61,8 +61,10 @@ fixed_decimal = { path = "../../utils/fixed_decimal" } icu_decimal = { path = "../../components/decimal/" } icu_locale_canonicalizer = { path = "../../components/locale_canonicalizer" } icu_locid = { path = "../../components/locid" } +icu_locid_macros = { path = "../../components/locid/macros" } icu_plurals = { path = "../../components/plurals/" } icu_provider = { path = "../../provider/core", features = ["provider_serde"] } +icu_provider_blob = { path = "../../provider/blob" } tinystr = { version = "0.4.10", features = ["alloc"], default-features = false } writeable = { path = "../../utils/writeable/" } diff --git a/ffi/capi/examples/fixeddecimal/.gitignore b/ffi/capi/examples/fixeddecimal/.gitignore index 3def44c6705..cba7efc8efd 100644 --- a/ffi/capi/examples/fixeddecimal/.gitignore +++ b/ffi/capi/examples/fixeddecimal/.gitignore @@ -1,6 +1 @@ a.out -optim* -*.i64 -*.dot -*.elf -*.o diff --git a/ffi/capi/examples/fixeddecimal/Makefile b/ffi/capi/examples/fixeddecimal/Makefile index d7a045d339d..528cd69245f 100644 --- a/ffi/capi/examples/fixeddecimal/Makefile +++ b/ffi/capi/examples/fixeddecimal/Makefile @@ -16,54 +16,10 @@ $(ALL_HEADERS): ../../../../target/debug/libicu_capi.a: $(ALL_RUST) cargo build -../../../../target/x86_64-unknown-linux-gnu/debug/libicu_capi.a: $(ALL_RUST) - RUSTFLAGS="-Clinker-plugin-lto -Clinker=clang -Clink-arg=-flto -Cpanic=abort" cargo +nightly-2021-02-28 panic-abort-build --target x86_64-unknown-linux-gnu --no-default-features --features x86tiny - -../../../../target/x86_64-unknown-linux-gnu/release/libicu_capi.a: $(ALL_RUST) - RUSTFLAGS="-Clinker-plugin-lto -Clinker=clang -Clink-arg=-flto -Cpanic=abort" cargo +nightly-2021-02-28 panic-abort-build --target x86_64-unknown-linux-gnu --no-default-features --features x86tiny --features smaller_static --release - -# Naive target: no optimizations, full std a.out: ../../../../target/debug/libicu_capi.a $(ALL_HEADERS) test.c gcc test.c ../../../../target/debug/libicu_capi.a -ldl -lpthread -lm -g -# optim.elf: gcc with maximum link-time code stripping (gc-sections and strip-all) -optim.elf: ../../../../target/debug/libicu_capi.a $(ALL_HEADERS) test.c - gcc -fdata-sections -ffunction-sections test.c ../../../../target/debug/libicu_capi.a -ldl -lpthread -lm -g -o optim.elf -Wl,--gc-sections -Wl,--strip-all - -# optim2.elf: clang single-step with gc-sections -optim2.elf: ../../../../target/x86_64-unknown-linux-gnu/debug/libicu_capi.a $(ALL_HEADERS) test.c - clang -flto -fdata-sections -ffunction-sections test.c ../../../../target/x86_64-unknown-linux-gnu/debug/libicu_capi.a -g -o optim2.elf -Wl,--gc-sections - -optim3.o: $(ALL_HEADERS) - clang -c -flto=thin -fdata-sections -ffunction-sections --target=x86_64-unknown-linux-gnu test.c -g -o optim3.o - -# optim3.elf: clang two-step with lld, debug mode -optim3.elf: optim3.o ../../../../target/x86_64-unknown-linux-gnu/debug/libicu_capi.a - clang -flto=thin -fuse-ld=lld -L . -o optim3.elf optim3.o ../../../../target/x86_64-unknown-linux-gnu/debug/libicu_capi.a -Wl,--gc-sections - -optim4.o: $(ALL_HEADERS) - clang -c -flto=thin -fdata-sections -ffunction-sections --target=x86_64-unknown-linux-gnu test.c -g -o optim4.o - -# optim4.elf: clang two-step with lld, release mode with debug symbols -optim4.elf: optim4.o ../../../../target/x86_64-unknown-linux-gnu/release/libicu_capi.a - clang -flto=thin -fuse-ld=lld -L . -o optim4.elf optim4.o ../../../../target/x86_64-unknown-linux-gnu/release/libicu_capi.a -Wl,--gc-sections - -optim5.o: $(ALL_HEADERS) - clang -c -flto=thin -fdata-sections -ffunction-sections --target=x86_64-unknown-linux-gnu test.c -o optim5.o - -# optim5.elf: clang two-step with lld, release mode stripped of debug symbols -optim5.elf: optim5.o ../../../../target/x86_64-unknown-linux-gnu/release/libicu_capi.a - clang -flto=thin -fuse-ld=lld -L . -o optim5.elf optim5.o ../../../../target/x86_64-unknown-linux-gnu/release/libicu_capi.a -Wl,--gc-sections -Wl,--strip-all - build: a.out test: build ./a.out - -build-optim: optim.elf optim2.elf optim3.elf optim4.elf optim5.elf - -# note: optim2.elf and optim3.elf crash when run with error "Illegal instruction" (investigate?) -test-optim: build-optim - ./optim.elf - ./optim4.elf - ./optim5.elf diff --git a/ffi/capi/examples/fixeddecimal_tiny/.gitignore b/ffi/capi/examples/fixeddecimal_tiny/.gitignore new file mode 100644 index 00000000000..3def44c6705 --- /dev/null +++ b/ffi/capi/examples/fixeddecimal_tiny/.gitignore @@ -0,0 +1,6 @@ +a.out +optim* +*.i64 +*.dot +*.elf +*.o diff --git a/ffi/capi/examples/fixeddecimal_tiny/Makefile b/ffi/capi/examples/fixeddecimal_tiny/Makefile new file mode 100644 index 00000000000..0d07fed03bb --- /dev/null +++ b/ffi/capi/examples/fixeddecimal_tiny/Makefile @@ -0,0 +1,65 @@ +# This file is part of ICU4X. For terms of use, please see the file +# called LICENSE at the top level of the ICU4X source tree +# (online at: https://github.com/unicode-org/icu4x/blob/main/LICENSE ). + +.DEFAULT_GOAL := test +.PHONY: build test + +ALL_HEADERS := $(wildcard ../../include/*.h) +ALL_RUST := $(wildcard ../../src/*.rs) + +$(ALL_RUST): + +$(ALL_HEADERS): + + +../../../../target/debug/libicu_capi.a: $(ALL_RUST) + cargo build + +../../../../target/x86_64-unknown-linux-gnu/debug/libicu_capi.a: $(ALL_RUST) + RUSTFLAGS="-Clinker-plugin-lto -Clinker=clang -Clink-arg=-flto -Cpanic=abort" cargo +nightly-2021-02-28 panic-abort-build --target x86_64-unknown-linux-gnu --no-default-features --features x86tiny + +../../../../target/x86_64-unknown-linux-gnu/release/libicu_capi.a: $(ALL_RUST) + RUSTFLAGS="-Clinker-plugin-lto -Clinker=clang -Clink-arg=-flto -Cpanic=abort" cargo +nightly-2021-02-28 panic-abort-build --target x86_64-unknown-linux-gnu --no-default-features --features x86tiny --features smaller_static --release + +# Naive target: no optimizations, full std +optim0.elf: ../../../../target/debug/libicu_capi.a $(ALL_HEADERS) test.c + gcc test.c ../../../../target/debug/libicu_capi.a -ldl -lpthread -lm -g -o optim0.elf + +# optim.elf: gcc with maximum link-time code stripping (gc-sections and strip-all) +optim1.elf: ../../../../target/debug/libicu_capi.a $(ALL_HEADERS) test.c + gcc -fdata-sections -ffunction-sections test.c ../../../../target/debug/libicu_capi.a -ldl -lpthread -lm -g -o optim1.elf -Wl,--gc-sections -Wl,--strip-all + +# optim2.elf: clang single-step with gc-sections +optim2.elf: ../../../../target/x86_64-unknown-linux-gnu/debug/libicu_capi.a $(ALL_HEADERS) test.c + clang -flto -fdata-sections -ffunction-sections test.c ../../../../target/x86_64-unknown-linux-gnu/debug/libicu_capi.a -g -o optim2.elf -Wl,--gc-sections + +optim3.o: $(ALL_HEADERS) + clang -c -flto=thin -fdata-sections -ffunction-sections --target=x86_64-unknown-linux-gnu test.c -g -o optim3.o + +# optim3.elf: clang two-step with lld, debug mode +optim3.elf: optim3.o ../../../../target/x86_64-unknown-linux-gnu/debug/libicu_capi.a + clang -flto=thin -fuse-ld=lld -L . -o optim3.elf optim3.o ../../../../target/x86_64-unknown-linux-gnu/debug/libicu_capi.a -Wl,--gc-sections + +optim4.o: $(ALL_HEADERS) + clang -c -flto=thin -fdata-sections -ffunction-sections --target=x86_64-unknown-linux-gnu test.c -g -o optim4.o + +# optim4.elf: clang two-step with lld, release mode with debug symbols +optim4.elf: optim4.o ../../../../target/x86_64-unknown-linux-gnu/release/libicu_capi.a + clang -flto=thin -fuse-ld=lld -L . -o optim4.elf optim4.o ../../../../target/x86_64-unknown-linux-gnu/release/libicu_capi.a -Wl,--gc-sections + +optim5.o: $(ALL_HEADERS) + clang -c -flto=thin -fdata-sections -ffunction-sections --target=x86_64-unknown-linux-gnu test.c -o optim5.o + +# optim5.elf: clang two-step with lld, release mode stripped of debug symbols +optim5.elf: optim5.o ../../../../target/x86_64-unknown-linux-gnu/release/libicu_capi.a + clang -flto=thin -fuse-ld=lld -L . -o optim5.elf optim5.o ../../../../target/x86_64-unknown-linux-gnu/release/libicu_capi.a -Wl,--gc-sections -Wl,--strip-all + +build: optim0.elf optim1.elf optim2.elf optim3.elf optim4.elf optim5.elf + +# note: optim2.elf and optim3.elf crash when run with error "Illegal instruction" (investigate?) +test: build + ./optim0.elf + ./optim1.elf + ./optim4.elf + ./optim5.elf diff --git a/ffi/capi/examples/fixeddecimal_tiny/README.md b/ffi/capi/examples/fixeddecimal_tiny/README.md new file mode 100644 index 00000000000..1adc91d9f5e --- /dev/null +++ b/ffi/capi/examples/fixeddecimal_tiny/README.md @@ -0,0 +1,15 @@ +# Tiny FixedDecimal FFI Demo + +This example contains tooling to build a size-optimized binary using FixedDecimal and FixedDecimalFormat in C over FFI. + +Prerequisites: `clang` and `lld`, which must be compatible with the Rust toolchain. `apt-get install clang lld` *might* work, but if you run into errors, refer to the following thread for tips: + +https://github.com/rust-lang/rust/issues/60059 + +You also need to install the correct toolchains: + +```bash +$ rustup install nightly-2021-02-28 +$ rustup component add --toolchain nightly-2021-02-28 rust-src +$ rustup target add x86_64-unknown-linux-gnu --toolchain nightly-2021-02-28 +``` diff --git a/ffi/capi/examples/fixeddecimal_tiny/test.c b/ffi/capi/examples/fixeddecimal_tiny/test.c new file mode 100644 index 00000000000..5c5b58803f0 --- /dev/null +++ b/ffi/capi/examples/fixeddecimal_tiny/test.c @@ -0,0 +1,50 @@ +// This file is part of ICU4X. For terms of use, please see the file +// called LICENSE at the top level of the ICU4X source tree +// (online at: https://github.com/unicode-org/icu4x/blob/main/LICENSE ). + +#include "../../include/ICU4XFixedDecimalFormat.h" +#include +#include + +int main() { + ICU4XLocale* locale = ICU4XLocale_create_bn(); + ICU4XCreateStaticDataProviderResult result = ICU4XStaticDataProvider_create(); + if (!result.success) { + printf("Failed to create FsDataProvider\n"); + return 1; + } + ICU4XStaticDataProvider* provider = result.provider; + ICU4XFixedDecimal* decimal = ICU4XFixedDecimal_create(1000007); + + ICU4XFixedDecimalFormatOptions opts = {ICU4XFixedDecimalGroupingStrategy_Auto, ICU4XFixedDecimalSignDisplay_Auto}; + + ICU4XFixedDecimalFormatResult fdf_result = ICU4XFixedDecimalFormat_try_new_from_static(locale, provider, opts); + if (!fdf_result.success) { + printf("Failed to create FixedDecimalFormat\n"); + return 1; + } + ICU4XFixedDecimalFormat* fdf = fdf_result.fdf; + char output[40]; + + DiplomatWriteable write = diplomat_simple_writeable(output, 40); + + bool success = ICU4XFixedDecimalFormat_format(fdf, decimal, &write).is_ok; + if (!success) { + printf("Failed to write result of FixedDecimalFormat::format to string.\n"); + return 1; + } + printf("Output is %s\n", output); + + const char* expected = u8"১০,০০,০০৭"; + if (strcmp(output, expected) != 0) { + printf("Output does not match expected output!\n"); + return 1; + } + + ICU4XFixedDecimal_destroy(decimal); + ICU4XFixedDecimalFormat_destroy(fdf); + ICU4XLocale_destroy(locale); + ICU4XStaticDataProvider_destroy(provider); + + return 0; +} diff --git a/ffi/capi/include/ICU4XCreateStaticDataProviderResult.h b/ffi/capi/include/ICU4XCreateStaticDataProviderResult.h new file mode 100644 index 00000000000..9bc03f9040d --- /dev/null +++ b/ffi/capi/include/ICU4XCreateStaticDataProviderResult.h @@ -0,0 +1,24 @@ +#ifndef ICU4XCreateStaticDataProviderResult_H +#define ICU4XCreateStaticDataProviderResult_H +#include +#include +#include +#include +#include "diplomat_runtime.h" + +#ifdef __cplusplus +extern "C" { +#endif +typedef struct ICU4XStaticDataProvider ICU4XStaticDataProvider; + +typedef struct ICU4XCreateStaticDataProviderResult { + ICU4XStaticDataProvider* provider; + bool success; +} ICU4XCreateStaticDataProviderResult; + +void ICU4XCreateStaticDataProviderResult_destroy(ICU4XCreateStaticDataProviderResult* self); + +#ifdef __cplusplus +} +#endif +#endif diff --git a/ffi/capi/include/ICU4XFixedDecimalFormat.h b/ffi/capi/include/ICU4XFixedDecimalFormat.h index 83e53df2b68..08172977365 100644 --- a/ffi/capi/include/ICU4XFixedDecimalFormat.h +++ b/ffi/capi/include/ICU4XFixedDecimalFormat.h @@ -15,11 +15,14 @@ typedef struct ICU4XFixedDecimalFormat ICU4XFixedDecimalFormat; #include "ICU4XDataProvider.h" #include "ICU4XFixedDecimalFormatOptions.h" #include "ICU4XFixedDecimalFormatResult.h" +#include "ICU4XStaticDataProvider.h" #include "ICU4XFixedDecimal.h" #include "result_void_void.h" ICU4XFixedDecimalFormatResult ICU4XFixedDecimalFormat_try_new(const ICU4XLocale* locale, const ICU4XDataProvider* provider, ICU4XFixedDecimalFormatOptions options); +ICU4XFixedDecimalFormatResult ICU4XFixedDecimalFormat_try_new_from_static(const ICU4XLocale* locale, const ICU4XStaticDataProvider* provider, ICU4XFixedDecimalFormatOptions options); + decimal_ffi_result_void_void ICU4XFixedDecimalFormat_format(const ICU4XFixedDecimalFormat* self, const ICU4XFixedDecimal* value, DiplomatWriteable* write); void ICU4XFixedDecimalFormat_destroy(ICU4XFixedDecimalFormat* self); diff --git a/ffi/capi/include/ICU4XLocale.h b/ffi/capi/include/ICU4XLocale.h index 1039f0a39f3..dd6866be15c 100644 --- a/ffi/capi/include/ICU4XLocale.h +++ b/ffi/capi/include/ICU4XLocale.h @@ -15,6 +15,10 @@ typedef struct ICU4XLocale ICU4XLocale; ICU4XLocale* ICU4XLocale_create(const char* name_data, size_t name_len); +ICU4XLocale* ICU4XLocale_create_en(); + +ICU4XLocale* ICU4XLocale_create_bn(); + ICU4XLocale* ICU4XLocale_clone(const ICU4XLocale* self); locale_ffi_result_void_ICU4XLocaleError ICU4XLocale_basename(const ICU4XLocale* self, DiplomatWriteable* write); diff --git a/ffi/capi/include/ICU4XStaticDataProvider.h b/ffi/capi/include/ICU4XStaticDataProvider.h new file mode 100644 index 00000000000..c47876813a0 --- /dev/null +++ b/ffi/capi/include/ICU4XStaticDataProvider.h @@ -0,0 +1,22 @@ +#ifndef ICU4XStaticDataProvider_H +#define ICU4XStaticDataProvider_H +#include +#include +#include +#include +#include "diplomat_runtime.h" + +#ifdef __cplusplus +extern "C" { +#endif + +typedef struct ICU4XStaticDataProvider ICU4XStaticDataProvider; +#include "ICU4XCreateStaticDataProviderResult.h" + +ICU4XCreateStaticDataProviderResult ICU4XStaticDataProvider_create(); +void ICU4XStaticDataProvider_destroy(ICU4XStaticDataProvider* self); + +#ifdef __cplusplus +} +#endif +#endif diff --git a/ffi/capi/src/decimal.rs b/ffi/capi/src/decimal.rs index a39f8fabe5b..a1a6de8a66f 100644 --- a/ffi/capi/src/decimal.rs +++ b/ffi/capi/src/decimal.rs @@ -8,13 +8,15 @@ pub mod ffi { use diplomat_runtime::DiplomatResult; use icu_decimal::{ options::{FixedDecimalFormatOptions, GroupingStrategy, SignDisplay}, + provider::DecimalSymbolsV1Marker, FixedDecimalFormat, }; + use icu_provider::prelude::DataProvider; use writeable::Writeable; use crate::{ fixed_decimal::ffi::ICU4XFixedDecimal, locale::ffi::ICU4XLocale, - provider::ffi::ICU4XDataProvider, + provider::ffi::ICU4XDataProvider, provider::ffi::ICU4XStaticDataProvider, }; #[diplomat::opaque] @@ -65,8 +67,30 @@ pub mod ffi { provider: &ICU4XDataProvider, options: ICU4XFixedDecimalFormatOptions, ) -> ICU4XFixedDecimalFormatResult { - let langid = locale.0.as_ref().clone(); let provider = provider.0.as_ref(); + Self::try_new_impl(locale, provider, options) + } + + /// Creates a new [`ICU4XFixedDecimalFormat`] from a [`ICU4XStaticDataProvider`]. + /// See [the Rust docs](https://unicode-org.github.io/icu4x-docs/doc/icu/decimal/struct.FixedDecimalFormat.html#method.try_new) for more information. + pub fn try_new_from_static( + locale: &ICU4XLocale, + provider: &ICU4XStaticDataProvider, + options: ICU4XFixedDecimalFormatOptions, + ) -> ICU4XFixedDecimalFormatResult { + let provider = provider.0.as_ref(); + Self::try_new_impl(locale, provider, options) + } + + fn try_new_impl( + locale: &ICU4XLocale, + provider: &D, + options: ICU4XFixedDecimalFormatOptions, + ) -> ICU4XFixedDecimalFormatResult + where + D: DataProvider<'static, DecimalSymbolsV1Marker> + ?Sized, + { + let langid = locale.0.as_ref().clone(); if let Result::Ok(fdf) = FixedDecimalFormat::try_new( langid, diff --git a/ffi/capi/src/locale.rs b/ffi/capi/src/locale.rs index 9941c6d81e0..412461b66c1 100644 --- a/ffi/capi/src/locale.rs +++ b/ffi/capi/src/locale.rs @@ -31,6 +31,16 @@ pub mod ffi { .map(|l| Box::new(ICU4XLocale(l))) } + /// Construct an [`ICU4XLocale`] for the English language. + pub fn create_en() -> Box { + Box::new(ICU4XLocale(icu_locid_macros::langid!("en").into())) + } + + /// Construct an [`ICU4XLocale`] for the Bangla language. + pub fn create_bn() -> Box { + Box::new(ICU4XLocale(icu_locid_macros::langid!("bn").into())) + } + /// Clones the [`ICU4XLocale`]. /// See [the Rust docs](https://unicode-org.github.io/icu4x-docs/doc/icu/locid/struct.Locale.html) for more information. #[allow(clippy::should_implement_trait)] diff --git a/ffi/capi/src/provider.rs b/ffi/capi/src/provider.rs index 0d8685aa226..9d930cca740 100644 --- a/ffi/capi/src/provider.rs +++ b/ffi/capi/src/provider.rs @@ -12,6 +12,7 @@ pub mod ffi { use alloc::string::ToString; use icu_provider::serde::SerdeDeDataProvider; + use icu_provider_blob::StaticDataProvider; #[cfg(all( feature = "provider_fs", not(any(target_arch = "wasm32", target_os = "none")) @@ -32,7 +33,7 @@ pub mod ffi { } impl ICU4XDataProvider { - /// Constructs an `FsDataProvider` and retirns it as an [`ICU4XDataProvider`]. + /// Constructs an `FsDataProvider` and returns it as an [`ICU4XDataProvider`]. /// See [the Rust docs](https://unicode-org.github.io/icu4x-docs/doc/icu_provider_fs/struct.FsDataProvider.html) for more details. #[allow(unused_variables)] pub fn create_fs(path: &str) -> ICU4XCreateDataProviderResult { @@ -61,7 +62,7 @@ pub mod ffi { unimplemented!(); } - /// Constructs an `StaticDataProvider` and retirns it as an [`ICU4XDataProvider`]. + /// Constructs an `StaticDataProvider` and returns it as an [`ICU4XDataProvider`]. /// See [the Rust docs](https://unicode-org.github.io/icu4x-docs/doc/icu_provider_blob/struct.StaticDataProvider.html) for more details. pub fn create_static() -> ICU4XCreateDataProviderResult { #[cfg(not(feature = "provider_static"))] @@ -81,4 +82,42 @@ pub mod ffi { } } } + + #[diplomat::opaque] + /// An ICU4X data provider backed by static data. This is a specialization of + /// [`ICU4XDataProvider`] intended to reduce code size. + /// + /// See [the Rust docs](https://unicode-org.github.io/icu4x-docs/doc/icu_provider_blob/struct.StaticDataProvider.html) for more information. + pub struct ICU4XStaticDataProvider(pub Box); + + /// A result type for `ICU4XStaticDataProvider::create`. + pub struct ICU4XCreateStaticDataProviderResult { + /// Will be `None` if `success` is `false`, do not use in that case. + pub provider: Option>, + // May potentially add a better error type in the future + pub success: bool, + } + + impl ICU4XStaticDataProvider { + /// Constructs an `StaticDataProvider` and returns it as an [`ICU4XStaticDataProvider`]. + /// + /// See [the Rust docs](https://unicode-org.github.io/icu4x-docs/doc/icu_provider_blob/struct.StaticDataProvider.html) for more details. + pub fn create() -> ICU4XCreateStaticDataProviderResult { + #[cfg(not(feature = "provider_static"))] + unimplemented!(); + + #[cfg(feature = "provider_static")] + { + #[cfg(feature = "smaller_static")] + let provider = icu_testdata::get_smaller_static_provider(); + #[cfg(not(feature = "smaller_static"))] + let provider = icu_testdata::get_static_provider(); + let erased = Box::new(provider); + ICU4XCreateStaticDataProviderResult { + provider: Some(Box::new(ICU4XStaticDataProvider(erased))), + success: true, + } + } + } + } } diff --git a/ffi/cpp/docs/source/decimal_ffi.rst b/ffi/cpp/docs/source/decimal_ffi.rst index bf80e764c5b..1bdd2db31ff 100644 --- a/ffi/cpp/docs/source/decimal_ffi.rst +++ b/ffi/cpp/docs/source/decimal_ffi.rst @@ -9,6 +9,10 @@ Creates a new :cpp:class:`ICU4XFixedDecimalFormat` from locale data. See `the Rust docs `__ for more information. + .. cpp:function:: static ICU4XFixedDecimalFormatResult try_new_from_static(const ICU4XLocale& locale, const ICU4XStaticDataProvider& provider, ICU4XFixedDecimalFormatOptions options) + + Creates a new :cpp:class:`ICU4XFixedDecimalFormat` from a :cpp:class:`ICU4XStaticDataProvider`. See `the Rust docs `__ for more information. + .. cpp:function:: template diplomat::result format_to_writeable(const ICU4XFixedDecimal& value, W& write) Formats a :cpp:class:`ICU4XFixedDecimal` to a string. See `the Rust docs `__ for more information. diff --git a/ffi/cpp/docs/source/locale_ffi.rst b/ffi/cpp/docs/source/locale_ffi.rst index 3c7edaafa5a..e8ba809ca75 100644 --- a/ffi/cpp/docs/source/locale_ffi.rst +++ b/ffi/cpp/docs/source/locale_ffi.rst @@ -9,6 +9,14 @@ Construct an :cpp:class:`ICU4XLocale` from an locale identifier. See `the Rust docs `__ for more information. + .. cpp:function:: static ICU4XLocale create_en() + + Construct an :cpp:class:`ICU4XLocale` for the English language. + + .. cpp:function:: static ICU4XLocale create_bn() + + Construct an :cpp:class:`ICU4XLocale` for the Bangla language. + .. cpp:function:: ICU4XLocale clone() Clones the :cpp:class:`ICU4XLocale`. See `the Rust docs `__ for more information. diff --git a/ffi/cpp/docs/source/provider_ffi.rst b/ffi/cpp/docs/source/provider_ffi.rst index fa2a741d83e..c1013a55070 100644 --- a/ffi/cpp/docs/source/provider_ffi.rst +++ b/ffi/cpp/docs/source/provider_ffi.rst @@ -11,14 +11,34 @@ .. cpp:member:: bool success +.. cpp:struct:: ICU4XCreateStaticDataProviderResult + + A result type for ``ICU4XStaticDataProvider::create``. + + .. cpp:member:: std::optional provider + + Will be ``None`` if ``success`` is ``false``, do not use in that case. + + .. cpp:member:: bool success + .. cpp:class:: ICU4XDataProvider An ICU4X data provider, capable of loading ICU4X data keys from some source. See `the Rust docs `__ for more information. .. cpp:function:: static ICU4XCreateDataProviderResult create_fs(const std::string_view path) - Constructs an ``FsDataProvider`` and retirns it as an :cpp:class:`ICU4XDataProvider`. See `the Rust docs `__ for more details. + Constructs an ``FsDataProvider`` and returns it as an :cpp:class:`ICU4XDataProvider`. See `the Rust docs `__ for more details. .. cpp:function:: static ICU4XCreateDataProviderResult create_static() - Constructs an ``StaticDataProvider`` and retirns it as an :cpp:class:`ICU4XDataProvider`. See `the Rust docs `__ for more details. + Constructs an ``StaticDataProvider`` and returns it as an :cpp:class:`ICU4XDataProvider`. See `the Rust docs `__ for more details. + +.. cpp:class:: ICU4XStaticDataProvider + + An ICU4X data provider backed by static data. This is a specialization of :cpp:class:`ICU4XDataProvider` intended to reduce code size. + See `the Rust docs `__ for more information. + + .. cpp:function:: static ICU4XCreateStaticDataProviderResult create() + + Constructs an ``StaticDataProvider`` and returns it as an :cpp:class:`ICU4XStaticDataProvider`. + See `the Rust docs `__ for more details. diff --git a/ffi/cpp/include/ICU4XCreateFixedDecimalFormatDataProviderResult.h b/ffi/cpp/include/ICU4XCreateFixedDecimalFormatDataProviderResult.h new file mode 100644 index 00000000000..c6d3c8935e5 --- /dev/null +++ b/ffi/cpp/include/ICU4XCreateFixedDecimalFormatDataProviderResult.h @@ -0,0 +1,24 @@ +#ifndef ICU4XCreateFixedDecimalFormatDataProviderResult_H +#define ICU4XCreateFixedDecimalFormatDataProviderResult_H +#include +#include +#include +#include +#include "diplomat_runtime.h" + +#ifdef __cplusplus +extern "C" { +#endif +typedef struct ICU4XFixedDecimalFormatDataProvider ICU4XFixedDecimalFormatDataProvider; + +typedef struct ICU4XCreateFixedDecimalFormatDataProviderResult { + ICU4XFixedDecimalFormatDataProvider* provider; + bool success; +} ICU4XCreateFixedDecimalFormatDataProviderResult; + +void ICU4XCreateFixedDecimalFormatDataProviderResult_destroy(ICU4XCreateFixedDecimalFormatDataProviderResult* self); + +#ifdef __cplusplus +} +#endif +#endif diff --git a/ffi/cpp/include/ICU4XCreateStaticDataProviderResult.h b/ffi/cpp/include/ICU4XCreateStaticDataProviderResult.h new file mode 100644 index 00000000000..9bc03f9040d --- /dev/null +++ b/ffi/cpp/include/ICU4XCreateStaticDataProviderResult.h @@ -0,0 +1,24 @@ +#ifndef ICU4XCreateStaticDataProviderResult_H +#define ICU4XCreateStaticDataProviderResult_H +#include +#include +#include +#include +#include "diplomat_runtime.h" + +#ifdef __cplusplus +extern "C" { +#endif +typedef struct ICU4XStaticDataProvider ICU4XStaticDataProvider; + +typedef struct ICU4XCreateStaticDataProviderResult { + ICU4XStaticDataProvider* provider; + bool success; +} ICU4XCreateStaticDataProviderResult; + +void ICU4XCreateStaticDataProviderResult_destroy(ICU4XCreateStaticDataProviderResult* self); + +#ifdef __cplusplus +} +#endif +#endif diff --git a/ffi/cpp/include/ICU4XCreateStaticDataProviderResult.hpp b/ffi/cpp/include/ICU4XCreateStaticDataProviderResult.hpp new file mode 100644 index 00000000000..d06a4bcb765 --- /dev/null +++ b/ffi/cpp/include/ICU4XCreateStaticDataProviderResult.hpp @@ -0,0 +1,30 @@ +#ifndef ICU4XCreateStaticDataProviderResult_HPP +#define ICU4XCreateStaticDataProviderResult_HPP +#include +#include +#include +#include +#include +#include +#include +#include "diplomat_runtime.hpp" + +namespace capi { +#include "ICU4XCreateStaticDataProviderResult.h" +} + +class ICU4XStaticDataProvider; + +struct ICU4XCreateStaticDataProviderResultDeleter { + void operator()(capi::ICU4XCreateStaticDataProviderResult* l) const noexcept { + capi::ICU4XCreateStaticDataProviderResult_destroy(l); + } +}; +struct ICU4XCreateStaticDataProviderResult { + public: + std::optional provider; + bool success; +}; + + +#endif diff --git a/ffi/cpp/include/ICU4XFixedDecimalFormat.h b/ffi/cpp/include/ICU4XFixedDecimalFormat.h index 83e53df2b68..08172977365 100644 --- a/ffi/cpp/include/ICU4XFixedDecimalFormat.h +++ b/ffi/cpp/include/ICU4XFixedDecimalFormat.h @@ -15,11 +15,14 @@ typedef struct ICU4XFixedDecimalFormat ICU4XFixedDecimalFormat; #include "ICU4XDataProvider.h" #include "ICU4XFixedDecimalFormatOptions.h" #include "ICU4XFixedDecimalFormatResult.h" +#include "ICU4XStaticDataProvider.h" #include "ICU4XFixedDecimal.h" #include "result_void_void.h" ICU4XFixedDecimalFormatResult ICU4XFixedDecimalFormat_try_new(const ICU4XLocale* locale, const ICU4XDataProvider* provider, ICU4XFixedDecimalFormatOptions options); +ICU4XFixedDecimalFormatResult ICU4XFixedDecimalFormat_try_new_from_static(const ICU4XLocale* locale, const ICU4XStaticDataProvider* provider, ICU4XFixedDecimalFormatOptions options); + decimal_ffi_result_void_void ICU4XFixedDecimalFormat_format(const ICU4XFixedDecimalFormat* self, const ICU4XFixedDecimal* value, DiplomatWriteable* write); void ICU4XFixedDecimalFormat_destroy(ICU4XFixedDecimalFormat* self); diff --git a/ffi/cpp/include/ICU4XFixedDecimalFormat.hpp b/ffi/cpp/include/ICU4XFixedDecimalFormat.hpp index 40dce549ea6..f2fd5fcf278 100644 --- a/ffi/cpp/include/ICU4XFixedDecimalFormat.hpp +++ b/ffi/cpp/include/ICU4XFixedDecimalFormat.hpp @@ -17,6 +17,7 @@ class ICU4XLocale; class ICU4XDataProvider; struct ICU4XFixedDecimalFormatOptions; struct ICU4XFixedDecimalFormatResult; +class ICU4XStaticDataProvider; class ICU4XFixedDecimal; struct ICU4XFixedDecimalFormatDeleter { @@ -27,6 +28,7 @@ struct ICU4XFixedDecimalFormatDeleter { class ICU4XFixedDecimalFormat { public: static ICU4XFixedDecimalFormatResult try_new(const ICU4XLocale& locale, const ICU4XDataProvider& provider, ICU4XFixedDecimalFormatOptions options); + static ICU4XFixedDecimalFormatResult try_new_from_static(const ICU4XLocale& locale, const ICU4XStaticDataProvider& provider, ICU4XFixedDecimalFormatOptions options); template diplomat::result format_to_writeable(const ICU4XFixedDecimal& value, W& write); diplomat::result format(const ICU4XFixedDecimal& value); inline const capi::ICU4XFixedDecimalFormat* AsFFI() const { return this->inner.get(); } @@ -40,6 +42,7 @@ class ICU4XFixedDecimalFormat { #include "ICU4XDataProvider.hpp" #include "ICU4XFixedDecimalFormatOptions.hpp" #include "ICU4XFixedDecimalFormatResult.hpp" +#include "ICU4XStaticDataProvider.hpp" #include "ICU4XFixedDecimal.hpp" inline ICU4XFixedDecimalFormatResult ICU4XFixedDecimalFormat::try_new(const ICU4XLocale& locale, const ICU4XDataProvider& provider, ICU4XFixedDecimalFormatOptions options) { @@ -54,6 +57,18 @@ inline ICU4XFixedDecimalFormatResult ICU4XFixedDecimalFormat::try_new(const ICU4 } return ICU4XFixedDecimalFormatResult{ .fdf = std::move(diplomat_optional_out_value_fdf), .success = std::move(diplomat_raw_struct_out_value.success) }; } +inline ICU4XFixedDecimalFormatResult ICU4XFixedDecimalFormat::try_new_from_static(const ICU4XLocale& locale, const ICU4XStaticDataProvider& provider, ICU4XFixedDecimalFormatOptions options) { + ICU4XFixedDecimalFormatOptions diplomat_wrapped_struct_options = options; + capi::ICU4XFixedDecimalFormatResult diplomat_raw_struct_out_value = capi::ICU4XFixedDecimalFormat_try_new_from_static(locale.AsFFI(), provider.AsFFI(), capi::ICU4XFixedDecimalFormatOptions{ .grouping_strategy = static_cast(diplomat_wrapped_struct_options.grouping_strategy), .sign_display = static_cast(diplomat_wrapped_struct_options.sign_display) }); + auto diplomat_optional_raw_out_value_fdf = diplomat_raw_struct_out_value.fdf; + std::optional diplomat_optional_out_value_fdf; + if (diplomat_optional_raw_out_value_fdf != nullptr) { + diplomat_optional_out_value_fdf = ICU4XFixedDecimalFormat(diplomat_optional_raw_out_value_fdf); + } else { + diplomat_optional_out_value_fdf = std::nullopt; + } + return ICU4XFixedDecimalFormatResult{ .fdf = std::move(diplomat_optional_out_value_fdf), .success = std::move(diplomat_raw_struct_out_value.success) }; +} template inline diplomat::result ICU4XFixedDecimalFormat::format_to_writeable(const ICU4XFixedDecimal& value, W& write) { capi::DiplomatWriteable write_writer = diplomat::WriteableTrait::Construct(write); auto diplomat_result_raw_out_value = capi::ICU4XFixedDecimalFormat_format(this->inner.get(), value.AsFFI(), &write_writer); diff --git a/ffi/cpp/include/ICU4XFixedDecimalFormatDataProvider.h b/ffi/cpp/include/ICU4XFixedDecimalFormatDataProvider.h new file mode 100644 index 00000000000..2c1c9cf5de8 --- /dev/null +++ b/ffi/cpp/include/ICU4XFixedDecimalFormatDataProvider.h @@ -0,0 +1,22 @@ +#ifndef ICU4XFixedDecimalFormatDataProvider_H +#define ICU4XFixedDecimalFormatDataProvider_H +#include +#include +#include +#include +#include "diplomat_runtime.h" + +#ifdef __cplusplus +extern "C" { +#endif + +typedef struct ICU4XFixedDecimalFormatDataProvider ICU4XFixedDecimalFormatDataProvider; +#include "ICU4XCreateFixedDecimalFormatDataProviderResult.h" + +ICU4XCreateFixedDecimalFormatDataProviderResult ICU4XFixedDecimalFormatDataProvider_create_static(); +void ICU4XFixedDecimalFormatDataProvider_destroy(ICU4XFixedDecimalFormatDataProvider* self); + +#ifdef __cplusplus +} +#endif +#endif diff --git a/ffi/cpp/include/ICU4XLocale.h b/ffi/cpp/include/ICU4XLocale.h index 1039f0a39f3..dd6866be15c 100644 --- a/ffi/cpp/include/ICU4XLocale.h +++ b/ffi/cpp/include/ICU4XLocale.h @@ -15,6 +15,10 @@ typedef struct ICU4XLocale ICU4XLocale; ICU4XLocale* ICU4XLocale_create(const char* name_data, size_t name_len); +ICU4XLocale* ICU4XLocale_create_en(); + +ICU4XLocale* ICU4XLocale_create_bn(); + ICU4XLocale* ICU4XLocale_clone(const ICU4XLocale* self); locale_ffi_result_void_ICU4XLocaleError ICU4XLocale_basename(const ICU4XLocale* self, DiplomatWriteable* write); diff --git a/ffi/cpp/include/ICU4XLocale.hpp b/ffi/cpp/include/ICU4XLocale.hpp index 0a2c9deb181..12da7c95670 100644 --- a/ffi/cpp/include/ICU4XLocale.hpp +++ b/ffi/cpp/include/ICU4XLocale.hpp @@ -24,6 +24,8 @@ struct ICU4XLocaleDeleter { class ICU4XLocale { public: static std::optional create(const std::string_view name); + static ICU4XLocale create_en(); + static ICU4XLocale create_bn(); ICU4XLocale clone(); template diplomat::result basename_to_writeable(W& write); diplomat::result basename(); @@ -55,6 +57,12 @@ inline std::optional ICU4XLocale::create(const std::string_view nam } return diplomat_optional_out_value; } +inline ICU4XLocale ICU4XLocale::create_en() { + return ICU4XLocale(capi::ICU4XLocale_create_en()); +} +inline ICU4XLocale ICU4XLocale::create_bn() { + return ICU4XLocale(capi::ICU4XLocale_create_bn()); +} inline ICU4XLocale ICU4XLocale::clone() { return ICU4XLocale(capi::ICU4XLocale_clone(this->inner.get())); } diff --git a/ffi/cpp/include/ICU4XStaticDataProvider.h b/ffi/cpp/include/ICU4XStaticDataProvider.h new file mode 100644 index 00000000000..c47876813a0 --- /dev/null +++ b/ffi/cpp/include/ICU4XStaticDataProvider.h @@ -0,0 +1,22 @@ +#ifndef ICU4XStaticDataProvider_H +#define ICU4XStaticDataProvider_H +#include +#include +#include +#include +#include "diplomat_runtime.h" + +#ifdef __cplusplus +extern "C" { +#endif + +typedef struct ICU4XStaticDataProvider ICU4XStaticDataProvider; +#include "ICU4XCreateStaticDataProviderResult.h" + +ICU4XCreateStaticDataProviderResult ICU4XStaticDataProvider_create(); +void ICU4XStaticDataProvider_destroy(ICU4XStaticDataProvider* self); + +#ifdef __cplusplus +} +#endif +#endif diff --git a/ffi/cpp/include/ICU4XStaticDataProvider.hpp b/ffi/cpp/include/ICU4XStaticDataProvider.hpp new file mode 100644 index 00000000000..ed038ecc12f --- /dev/null +++ b/ffi/cpp/include/ICU4XStaticDataProvider.hpp @@ -0,0 +1,46 @@ +#ifndef ICU4XStaticDataProvider_HPP +#define ICU4XStaticDataProvider_HPP +#include +#include +#include +#include +#include +#include +#include +#include "diplomat_runtime.hpp" + +namespace capi { +#include "ICU4XStaticDataProvider.h" +} + +struct ICU4XCreateStaticDataProviderResult; + +struct ICU4XStaticDataProviderDeleter { + void operator()(capi::ICU4XStaticDataProvider* l) const noexcept { + capi::ICU4XStaticDataProvider_destroy(l); + } +}; +class ICU4XStaticDataProvider { + public: + static ICU4XCreateStaticDataProviderResult create(); + inline const capi::ICU4XStaticDataProvider* AsFFI() const { return this->inner.get(); } + inline capi::ICU4XStaticDataProvider* AsFFIMut() { return this->inner.get(); } + inline ICU4XStaticDataProvider(capi::ICU4XStaticDataProvider* i) : inner(i) {} + private: + std::unique_ptr inner; +}; + +#include "ICU4XCreateStaticDataProviderResult.hpp" + +inline ICU4XCreateStaticDataProviderResult ICU4XStaticDataProvider::create() { + capi::ICU4XCreateStaticDataProviderResult diplomat_raw_struct_out_value = capi::ICU4XStaticDataProvider_create(); + auto diplomat_optional_raw_out_value_provider = diplomat_raw_struct_out_value.provider; + std::optional diplomat_optional_out_value_provider; + if (diplomat_optional_raw_out_value_provider != nullptr) { + diplomat_optional_out_value_provider = ICU4XStaticDataProvider(diplomat_optional_raw_out_value_provider); + } else { + diplomat_optional_out_value_provider = std::nullopt; + } + return ICU4XCreateStaticDataProviderResult{ .provider = std::move(diplomat_optional_out_value_provider), .success = std::move(diplomat_raw_struct_out_value.success) }; +} +#endif diff --git a/ffi/wasm/docs.rst b/ffi/wasm/docs.rst new file mode 100644 index 00000000000..4f523315679 --- /dev/null +++ b/ffi/wasm/docs.rst @@ -0,0 +1,332 @@ + +.. js:class:: ICU4XCanonicalizationResult + + + FFI version of ``CanonicalizationResult``. See `the Rust docs `__ for more details. + +.. js:class:: ICU4XCreateDataProviderResult + + + A result type for ``ICU4XDataProvider::create``. + + .. js:attribute:: provider + + + Will be ``None`` if ``success`` is ``false``, do not use in that case. + + .. js:attribute:: success + + + +.. js:class:: ICU4XCreateFixedDecimalFormatDataProviderResult + + + A result type for ``ICU4XDataProvider::create``. + + .. js:attribute:: provider + + + Will be ``None`` if ``success`` is ``false``, do not use in that case. + + .. js:attribute:: success + + + +.. js:class:: ICU4XCreateFixedDecimalResult + + + + .. js:attribute:: fd + + + Will be None if ``success`` is ``false`` + + .. js:attribute:: success + + + Currently just a boolean, but we might add a proper error enum as necessary + +.. js:class:: ICU4XCreatePluralOperandsResult + + + This is the result returned by ``ICU4XPluralOperands::create()`` See `the Rust docs `__ for more details. + + .. js:attribute:: operands + + + + .. js:attribute:: success + + + +.. js:class:: ICU4XCreatePluralRulesResult + + + + .. js:attribute:: rules + + + + .. js:attribute:: success + + + +.. js:class:: ICU4XDataProvider + + + An ICU4X data provider, capable of loading ICU4X data keys from some source. See `the Rust docs `__ for more information. + + .. js:staticfunction:: create_fs(path) + + Constructs an ``FsDataProvider`` and retirns it as an :js:class:`ICU4XDataProvider`. See `the Rust docs `__ for more details. + + .. js:staticfunction:: create_static() + + Constructs an ``StaticDataProvider`` and retirns it as an :js:class:`ICU4XDataProvider`. See `the Rust docs `__ for more details. + +.. js:class:: ICU4XFixedDecimal + + + A decimal number. See `the Rust docs `__ for more information. + + .. js:staticfunction:: create(v) + + Construct an :js:class:`ICU4XFixedDecimal` from an integer. See `the Rust docs `__ for more information. + + .. js:staticfunction:: create_fromstr(v) + + Construct an :js:class:`ICU4XFixedDecimal` from a string. See `the Rust docs `__ for more information. + + .. js:function:: multiply_pow10(power) + + Multiply the :js:class:`ICU4XFixedDecimal` by a given power of ten. See `the Rust docs `__ for more information. + + .. js:function:: negate() + + Invert the sign of the :js:class:`ICU4XFixedDecimal`. See `the Rust docs `__ for more information. + + .. js:function:: to_string() + + Format the :js:class:`ICU4XFixedDecimal` as a string. See `the Rust docs `__ for more information. + +.. js:class:: ICU4XFixedDecimalFormat + + + An ICU4X Fixed Decimal Format object, capable of formatting a :js:class:`ICU4XFixedDecimal` as a string. See `the Rust docs `__ for more information. + + .. js:staticfunction:: try_new(locale, provider, options) + + Creates a new :js:class:`ICU4XFixedDecimalFormat` from locale data. See `the Rust docs `__ for more information. + + .. js:staticfunction:: try_new_specific(locale, provider, options) + + Creates a new :js:class:`ICU4XFixedDecimalFormat` from a data provider specific to FixedDecimalFormat. See `the Rust docs `__ for more information. + + .. js:function:: format(value) + + Formats a :js:class:`ICU4XFixedDecimal` to a string. See `the Rust docs `__ for more information. + +.. js:class:: ICU4XFixedDecimalFormatDataProvider + + + A DataProvider specific to FixedDecimalFormat. + + .. js:staticfunction:: create_static() + + Create a DataProvider reading from static data specific to FixedDecimalFormat. + +.. js:class:: ICU4XFixedDecimalFormatOptions + + + + .. js:attribute:: grouping_strategy + + + + .. js:attribute:: sign_display + + + + .. js:staticfunction:: default() + + +.. js:class:: ICU4XFixedDecimalFormatResult + + + + .. js:attribute:: fdf + + + The :js:class:`ICU4XFixedDecimalFormat`, exists if creation was successful. + + .. js:attribute:: success + + + Whether creating the :js:class:`ICU4XFixedDecimalFormat` was successful. + +.. js:class:: ICU4XFixedDecimalGroupingStrategy + + + +.. js:class:: ICU4XFixedDecimalSignDisplay + + + +.. js:class:: ICU4XLocale + + + An ICU4X Locale, capable of representing strings like ``"en-US"``. See `the Rust docs `__ for more information. + + .. js:staticfunction:: create(name) + + Construct an :js:class:`ICU4XLocale` from an locale identifier. See `the Rust docs `__ for more information. + + .. js:staticfunction:: create_en() + + Construct an :js:class:`ICU4XLocale` for the English language. + + .. js:staticfunction:: create_bn() + + Construct an :js:class:`ICU4XLocale` for the Bangla language. + + .. js:function:: clone() + + Clones the :js:class:`ICU4XLocale`. See `the Rust docs `__ for more information. + + .. js:function:: basename() + + Write a string representation of the ``LanguageIdentifier`` part of :js:class:`ICU4XLocale` to ``write``. See `the Rust docs `__ for more information. + + .. js:function:: get_unicode_extension(bytes) + + Write a string representation of the unicode extension to ``write`` See `the Rust docs `__ for more information. + + .. js:function:: language() + + Write a string representation of :js:class:`ICU4XLocale` language to ``write`` See `the Rust docs `__ for more information. + + .. js:function:: region() + + Write a string representation of :js:class:`ICU4XLocale` region to ``write`` See `the Rust docs `__ for more information. + + .. js:function:: script() + + Write a string representation of :js:class:`ICU4XLocale` script to ``write`` See `the Rust docs `__ for more information. + + .. js:function:: tostring() + + Write a string representation of :js:class:`ICU4XLocale` to ``write`` See `the Rust docs `__ for more information. + +.. js:class:: ICU4XLocaleCanonicalizer + + + A locale canonicalizer. See `the Rust docs `__ for more details. + + .. js:staticfunction:: create(provider) + + Create a new :js:class:`ICU4XLocaleCanonicalizer`. See `the Rust docs `__ for more details. + + .. js:function:: canonicalize(locale) + + FFI version of ``LocaleCanonicalizer::canonicalize()``. See `the Rust docs `__ for more details. + + .. js:function:: maximize(locale) + + FFI version of ``LocaleCanonicalizer::maximize()``. See `the Rust docs `__ for more details. + + .. js:function:: minimize(locale) + + FFI version of ``LocaleCanonicalizer::minimize()``. See `the Rust docs `__ for more details. + +.. js:class:: ICU4XLocaleError + + + +.. js:class:: ICU4XPluralCategories + + + FFI version of ``PluralRules::categories()`` data. + + .. js:attribute:: zero + + + + .. js:attribute:: one + + + + .. js:attribute:: two + + + + .. js:attribute:: few + + + + .. js:attribute:: many + + + + .. js:attribute:: other + + + +.. js:class:: ICU4XPluralCategory + + + FFI version of ``PluralCategory``. See `the Rust docs `__ for more details. + +.. js:class:: ICU4XPluralOperands + + + FFI version of ``PluralOperands``. See `the Rust docs `__ for more details. + + .. js:attribute:: i + + + + .. js:attribute:: v + + + + .. js:attribute:: w + + + + .. js:attribute:: f + + + + .. js:attribute:: t + + + + .. js:attribute:: c + + + + .. js:staticfunction:: create(s) + + FFI version of ``PluralOperands::from_str()``. See `the Rust docs `__ for more details. + +.. js:class:: ICU4XPluralRuleType + + + FFI version of ``PluralRuleType``. See `the Rust docs `__ for more details. + +.. js:class:: ICU4XPluralRules + + + FFI version of ``PluralRules``. See `the Rust docs `__ for more details. + + .. js:staticfunction:: create(locale, provider, ty) + + FFI version of ``PluralRules::try_new()``. See `the Rust docs `__ for more details. + + .. js:function:: select(op) + + FFI version of ``PluralRules::select()``. See `the Rust docs `__ for more details. + + .. js:function:: categories() + + FFI version of ``PluralRules::categories()``. See `the Rust docs `__ for more details. diff --git a/ffi/wasm/docs/decimal_ffi.rst b/ffi/wasm/docs/decimal_ffi.rst index 43d53dc1aa8..6e0ec4081a8 100644 --- a/ffi/wasm/docs/decimal_ffi.rst +++ b/ffi/wasm/docs/decimal_ffi.rst @@ -9,6 +9,10 @@ Creates a new :js:class:`ICU4XFixedDecimalFormat` from locale data. See `the Rust docs `__ for more information. + .. js:staticfunction:: try_new_from_static(locale, provider, options) + + Creates a new :js:class:`ICU4XFixedDecimalFormat` from a :js:class:`ICU4XStaticDataProvider`. See `the Rust docs `__ for more information. + .. js:function:: format(value) Formats a :js:class:`ICU4XFixedDecimal` to a string. See `the Rust docs `__ for more information. diff --git a/ffi/wasm/docs/locale_ffi.rst b/ffi/wasm/docs/locale_ffi.rst index 32fe17be420..97813434977 100644 --- a/ffi/wasm/docs/locale_ffi.rst +++ b/ffi/wasm/docs/locale_ffi.rst @@ -9,6 +9,14 @@ Construct an :js:class:`ICU4XLocale` from an locale identifier. See `the Rust docs `__ for more information. + .. js:staticfunction:: create_en() + + Construct an :js:class:`ICU4XLocale` for the English language. + + .. js:staticfunction:: create_bn() + + Construct an :js:class:`ICU4XLocale` for the Bangla language. + .. js:function:: clone() Clones the :js:class:`ICU4XLocale`. See `the Rust docs `__ for more information. diff --git a/ffi/wasm/docs/provider_ffi.rst b/ffi/wasm/docs/provider_ffi.rst index 401513bbdac..3fe9657f856 100644 --- a/ffi/wasm/docs/provider_ffi.rst +++ b/ffi/wasm/docs/provider_ffi.rst @@ -11,14 +11,34 @@ .. js:attribute:: success +.. js:class:: ICU4XCreateStaticDataProviderResult + + A result type for ``ICU4XStaticDataProvider::create``. + + .. js:attribute:: provider + + Will be ``None`` if ``success`` is ``false``, do not use in that case. + + .. js:attribute:: success + .. js:class:: ICU4XDataProvider An ICU4X data provider, capable of loading ICU4X data keys from some source. See `the Rust docs `__ for more information. .. js:staticfunction:: create_fs(path) - Constructs an ``FsDataProvider`` and retirns it as an :js:class:`ICU4XDataProvider`. See `the Rust docs `__ for more details. + Constructs an ``FsDataProvider`` and returns it as an :js:class:`ICU4XDataProvider`. See `the Rust docs `__ for more details. .. js:staticfunction:: create_static() - Constructs an ``StaticDataProvider`` and retirns it as an :js:class:`ICU4XDataProvider`. See `the Rust docs `__ for more details. + Constructs an ``StaticDataProvider`` and returns it as an :js:class:`ICU4XDataProvider`. See `the Rust docs `__ for more details. + +.. js:class:: ICU4XStaticDataProvider + + An ICU4X data provider backed by static data. This is a specialization of :js:class:`ICU4XDataProvider` intended to reduce code size. + See `the Rust docs `__ for more information. + + .. js:staticfunction:: create() + + Constructs an ``StaticDataProvider`` and returns it as an :js:class:`ICU4XStaticDataProvider`. + See `the Rust docs `__ for more details. diff --git a/ffi/wasm/lib/api.mjs b/ffi/wasm/lib/api.mjs index 921cf2bdbdd..7ca2de3256c 100644 --- a/ffi/wasm/lib/api.mjs +++ b/ffi/wasm/lib/api.mjs @@ -101,6 +101,28 @@ export class ICU4XCreatePluralRulesResult { } } +const ICU4XCreateStaticDataProviderResult_box_destroy_registry = new FinalizationRegistry(underlying => { + wasm.ICU4XCreateStaticDataProviderResult_destroy(underlying); +}); + +export class ICU4XCreateStaticDataProviderResult { + constructor(underlying) { + this.underlying = underlying; + } + + get provider() { + return (() => { + const out = new ICU4XStaticDataProvider((new Uint32Array(wasm.memory.buffer, this.underlying + 0, 1))[0]); + out.owner = null; + return out; + })(); + } + + get success() { + return (new Uint8Array(wasm.memory.buffer, this.underlying + 4, 1))[0] == 1; + } +} + const ICU4XDataProvider_box_destroy_registry = new FinalizationRegistry(underlying => { wasm.ICU4XDataProvider_destroy(underlying); }); @@ -239,6 +261,25 @@ export class ICU4XFixedDecimalFormat { return diplomat_out; } + static try_new_from_static(locale, provider, options) { + const diplomat_ICU4XFixedDecimalFormatOptions_extracted_grouping_strategy = options["grouping_strategy"]; + const diplomat_ICU4XFixedDecimalFormatOptions_extracted_sign_display = options["sign_display"]; + const diplomat_out = (() => { + const diplomat_receive_buffer = wasm.diplomat_alloc(5); + wasm.ICU4XFixedDecimalFormat_try_new_from_static(diplomat_receive_buffer, locale.underlying, provider.underlying, ICU4XFixedDecimalGroupingStrategy_js_to_rust[diplomat_ICU4XFixedDecimalFormatOptions_extracted_grouping_strategy], ICU4XFixedDecimalSignDisplay_js_to_rust[diplomat_ICU4XFixedDecimalFormatOptions_extracted_sign_display]); + const out = new ICU4XFixedDecimalFormatResult(diplomat_receive_buffer); + const out_fdf_value = out.fdf; + ICU4XFixedDecimalFormat_box_destroy_registry.register(out_fdf_value, out_fdf_value.underlying); + Object.defineProperty(out, "fdf", { value: out_fdf_value }); + diplomat_alloc_destroy_registry.register(out, { + ptr: out.underlying, + size: 5 + }); + return out; + })(); + return diplomat_out; + } + format(value) { const diplomat_out = diplomatRuntime.withWriteable(wasm, (writeable) => { return (() => { @@ -361,6 +402,32 @@ export class ICU4XLocale { return diplomat_out; } + static create_en() { + const diplomat_out = (() => { + const out = (() => { + const out = new ICU4XLocale(wasm.ICU4XLocale_create_en()); + out.owner = null; + return out; + })(); + ICU4XLocale_box_destroy_registry.register(out, out.underlying) + return out; + })(); + return diplomat_out; + } + + static create_bn() { + const diplomat_out = (() => { + const out = (() => { + const out = new ICU4XLocale(wasm.ICU4XLocale_create_bn()); + out.owner = null; + return out; + })(); + ICU4XLocale_box_destroy_registry.register(out, out.underlying) + return out; + })(); + return diplomat_out; + } + clone() { const diplomat_out = (() => { const out = (() => { @@ -723,3 +790,30 @@ export class ICU4XPluralRules { return diplomat_out; } } + +const ICU4XStaticDataProvider_box_destroy_registry = new FinalizationRegistry(underlying => { + wasm.ICU4XStaticDataProvider_destroy(underlying); +}); + +export class ICU4XStaticDataProvider { + constructor(underlying) { + this.underlying = underlying; + } + + static create() { + const diplomat_out = (() => { + const diplomat_receive_buffer = wasm.diplomat_alloc(5); + wasm.ICU4XStaticDataProvider_create(diplomat_receive_buffer); + const out = new ICU4XCreateStaticDataProviderResult(diplomat_receive_buffer); + const out_provider_value = out.provider; + ICU4XStaticDataProvider_box_destroy_registry.register(out_provider_value, out_provider_value.underlying); + Object.defineProperty(out, "provider", { value: out_provider_value }); + diplomat_alloc_destroy_registry.register(out, { + ptr: out.underlying, + size: 5 + }); + return out; + })(); + return diplomat_out; + } +} diff --git a/provider/blob/Cargo.toml b/provider/blob/Cargo.toml index 66798438c1c..a21741dd821 100644 --- a/provider/blob/Cargo.toml +++ b/provider/blob/Cargo.toml @@ -32,6 +32,7 @@ serde = { version = "1.0", default-features = false, features = ["alloc"] } postcard = { version = "0.7.0" } erased-serde = { version = "0.3", default-features = false, features = ["alloc"] } litemap = { version = "0.2.0", path = "../../utils/litemap/", features = ["serde"] } +writeable = { path = "../../utils/writeable" } # For the export feature log = { version = "0.4", optional = true } diff --git a/provider/blob/src/path_util.rs b/provider/blob/src/path_util.rs index 8ebf8084699..05aef42b004 100644 --- a/provider/blob/src/path_util.rs +++ b/provider/blob/src/path_util.rs @@ -2,13 +2,15 @@ // called LICENSE at the top level of the ICU4X source tree // (online at: https://github.com/unicode-org/icu4x/blob/main/LICENSE ). -use alloc::string::{String, ToString}; -use alloc::vec::Vec; +use alloc::string::String; use icu_provider::prelude::*; +use writeable::Writeable; pub fn resource_path_to_string(resource_path: &ResourcePath) -> String { - let key_components = resource_path.key.get_components(); - let opt_components = resource_path.options.get_components(); - let all_components: Vec<&str> = key_components.iter().chain(opt_components.iter()).collect(); - "/".to_string() + &all_components.join("/") + let mut output = String::with_capacity(resource_path.write_len().capacity() + 1); + output.push('/'); + resource_path + .write_to(&mut output) + .expect("impl Write for String is infallible"); + output } diff --git a/provider/core/src/resource.rs b/provider/core/src/resource.rs index b3a357de323..a425bc8fb09 100644 --- a/provider/core/src/resource.rs +++ b/provider/core/src/resource.rs @@ -16,6 +16,7 @@ use core::fmt; use core::fmt::Write; use icu_locid::LanguageIdentifier; use tinystr::{TinyStr16, TinyStr4}; +use writeable::{LengthHint, Writeable}; /// A top-level collection of related resource keys. #[non_exhaustive] @@ -133,35 +134,25 @@ impl fmt::Debug for ResourceKey { impl fmt::Display for ResourceKey { fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { - writeable::Writeable::write_to(self, f) + Writeable::write_to(self, f) } } -impl writeable::Writeable for ResourceKey { +impl Writeable for ResourceKey { fn write_to(&self, sink: &mut W) -> core::fmt::Result { sink.write_str(&self.category.as_str())?; sink.write_char('/')?; sink.write_str(self.sub_category.as_str())?; sink.write_char('@')?; - write!(sink, "{}", self.version)?; + self.version.write_to(sink)?; Ok(()) } - fn write_len(&self) -> writeable::LengthHint { - writeable::LengthHint::Exact(2) + fn write_len(&self) -> LengthHint { + LengthHint::Exact(2) + self.category.as_str().len() + self.sub_category.len() - + if self.version < 10 { - 1 - } else if self.version < 100 { - 2 - } else if self.version < 1000 { - 3 - } else if self.version < 10000 { - 4 - } else { - 5 - } + + self.version.write_len() } } diff --git a/tools/scripts/ffi.toml b/tools/scripts/ffi.toml index 4fd759acf70..b2708b50e16 100644 --- a/tools/scripts/ffi.toml +++ b/tools/scripts/ffi.toml @@ -10,6 +10,8 @@ category = "ICU4X Development" dependencies = [ "verify-diplomat-gen", "test-capi", + # TODO(#964): Enable this CI test. + # "test-capi-tiny", "test-cpp", "build-wearos-ffi", "test-nostd", @@ -44,13 +46,25 @@ script_runner = "@duckscript" script = ''' exit_on_error true cd ffi/capi/examples/pluralrules -exec make +exec --fail-on-error make cd ../fixeddecimal exec --fail-on-error make cd ../locale exec --fail-on-error make ''' +[tasks.test-capi-tiny] +description = "Run C API tests for tiny targets" +category = "ICU4X Development" +dependencies = [ "diplomat-gen-c" ] +script_runner = "@duckscript" +script = ''' +exit_on_error true +cd ffi/capi/examples/fixeddecimal_tiny +exec --fail-on-error make +exec ls -l +''' + [tasks.test-cpp] description = "Run CPP tests" category = "ICU4X Development" @@ -112,7 +126,7 @@ exec --fail-on-error diplomat-tool cpp ../cpp/include --docs ../cpp/docs/source [tasks.build-wearos-ffi] description = "Build ICU4X CAPI for Cortex" category = "ICU4X FFI" -toolchain = "nightly" +toolchain = "nightly-2021-02-28" env = { "RUSTFLAGS" = "-Ctarget-cpu=cortex-m33" } command = "cargo" args = ["build", "--package", "icu_capi", @@ -124,6 +138,6 @@ args = ["build", "--package", "icu_capi", [tasks.test-nostd] description = "Ensure ICU4X core builds on no-std" category = "ICU4X FFI" -toolchain = "nightly" +toolchain = "nightly-2021-02-28" command = "cargo" args = ["build", "--package", "icu", "--target", "thumbv7m-none-eabi"] diff --git a/utils/writeable/src/impls.rs b/utils/writeable/src/impls.rs new file mode 100644 index 00000000000..4d4f6d2244f --- /dev/null +++ b/utils/writeable/src/impls.rs @@ -0,0 +1,91 @@ +// This file is part of ICU4X. For terms of use, please see the file +// called LICENSE at the top level of the ICU4X source tree +// (online at: https://github.com/unicode-org/icu4x/blob/main/LICENSE ). + +use crate::LengthHint; +use crate::Writeable; +use core::convert::TryFrom; +use core::fmt; +use core::str; + +impl Writeable for u8 { + fn write_to(&self, sink: &mut W) -> fmt::Result { + let mut buf = [b'0'; 3]; + let mut n = *self; + let mut i = 3usize; + while n != 0 { + i -= 1; + buf[i] = b'0' + (n % 10); + n /= 10; + } + if i == 3 { + debug_assert_eq!(*self, 0); + i = 2; + } + let s = unsafe { str::from_utf8_unchecked(&buf[i..]) }; + sink.write_str(s) + } + + fn write_len(&self) -> LengthHint { + if *self < 10 { + LengthHint::Exact(1) + } else if *self < 100 { + LengthHint::Exact(2) + } else { + LengthHint::Exact(3) + } + } +} + +impl Writeable for u16 { + fn write_to(&self, sink: &mut W) -> fmt::Result { + let mut buf = [b'0'; 5]; + let mut n = *self; + let mut i = 5usize; + while n != 0 { + i -= 1; + buf[i] = b'0' + u8::try_from(n % 10).expect("<10"); + n /= 10; + } + if i == 5 { + debug_assert_eq!(*self, 0); + i = 4; + } + let s = unsafe { str::from_utf8_unchecked(&buf[i..]) }; + sink.write_str(s) + } + + fn write_len(&self) -> LengthHint { + if *self < 10 { + LengthHint::Exact(1) + } else if *self < 100 { + LengthHint::Exact(2) + } else if *self < 1000 { + LengthHint::Exact(3) + } else if *self < 10000 { + LengthHint::Exact(4) + } else { + LengthHint::Exact(5) + } + } +} + +#[test] +fn test_u8() { + use crate::assert_writeable_eq; + assert_writeable_eq!("0", &0u8); + assert_writeable_eq!("1", &1u8); + assert_writeable_eq!("10", &10u8); + assert_writeable_eq!("99", &99u8); + assert_writeable_eq!("255", &255u8); +} + +#[test] +fn test_u16() { + use crate::assert_writeable_eq; + assert_writeable_eq!("0", &0u16); + assert_writeable_eq!("1", &1u16); + assert_writeable_eq!("10", &10u16); + assert_writeable_eq!("99", &99u16); + assert_writeable_eq!("65535", &65535u16); +} diff --git a/utils/writeable/src/lib.rs b/utils/writeable/src/lib.rs index 3bf9712804c..7b8cb88f517 100644 --- a/utils/writeable/src/lib.rs +++ b/utils/writeable/src/lib.rs @@ -53,6 +53,7 @@ extern crate alloc; +mod impls; mod ops; use alloc::string::String;