• Home
  • Features
  • Pricing
  • Docs
  • Announcements
  • Sign In

zbraniecki / icu4x / 6815798908

09 Nov 2023 05:17PM CUT coverage: 72.607% (-2.4%) from 75.01%
6815798908

push

github

web-flow
Implement `Any/BufferProvider` for some smart pointers (#4255)

Allows storing them as a `Box<dyn Any/BufferProvider>` without using a
wrapper type that implements the trait.

44281 of 60987 relevant lines covered (72.61%)

201375.86 hits per line

Source File
Press 'n' to go to next uncovered line, 'b' for previous

88.06
/provider/blob/src/blob_data_provider.rs
1
// This file is part of ICU4X. For terms of use, please see the file
2
// called LICENSE at the top level of the ICU4X source tree
3
// (online at: https://github.com/unicode-org/icu4x/blob/main/LICENSE ).
4

5
use crate::blob_schema::BlobSchema;
6
use alloc::boxed::Box;
7
use icu_provider::buf::BufferFormat;
8
use icu_provider::prelude::*;
9
use icu_provider::Cart;
10
use yoke::*;
11

12
/// A data provider that reads from serialized blobs of data.
13
///
14
/// This enables data blobs to be read from arbitrary sources at runtime, allowing code and data
15
/// to be separated. Alternatively, blobs can also be statically included at compile time.
16
///
17
/// [`BlobDataProvider`] implements [`BufferProvider`], so it can be used in
18
/// `*_with_buffer_provider` constructors across ICU4X.
19
///
20
/// # `Sync + Send`
21
///
22
/// This provider uses reference counting internally. When the `sync` Cargo feature on the [`icu_provider`]
23
/// crate is enabled, it uses [`Arc`](alloc::sync::Arc) instead of [`Rc`](alloc::rc::Rc), making
24
/// it `Sync + Send`.
25
///
26
/// # Examples
27
///
28
/// ## Dynamic loading
29
///
30
/// Load "hello world" data from a postcard blob loaded at runtime:
31
///
32
/// ```
33
/// use icu_locid::locale;
34
/// use icu_provider::hello_world::HelloWorldFormatter;
35
/// use icu_provider_blob::BlobDataProvider;
36
/// use writeable::assert_writeable_eq;
37
///
38
/// // Read an ICU4X data blob dynamically:
39
/// let blob = std::fs::read(concat!(
40
///     env!("CARGO_MANIFEST_DIR"),
41
///     "/tests/data/v2.postcard",
42
/// ))
43
/// .expect("Reading pre-computed postcard buffer");
44
///
45
/// // Create a DataProvider from it:
46
/// let provider = BlobDataProvider::try_new_from_blob(blob.into_boxed_slice())
47
///     .expect("Deserialization should succeed");
48
///
49
/// // Check that it works:
50
/// let formatter = HelloWorldFormatter::try_new_with_buffer_provider(
51
///     &provider,
52
///     &locale!("la").into(),
53
/// )
54
/// .expect("locale exists");
55
///
56
/// assert_writeable_eq!(formatter.format(), "Ave, munde");
57
/// ```
58
///
59
/// ## Static loading
60
///
61
/// Load "hello world" data from a postcard blob statically linked at compile time:
62
///
63
/// ```
64
/// use icu_locid::locale;
65
/// use icu_provider::hello_world::HelloWorldFormatter;
66
/// use icu_provider_blob::BlobDataProvider;
67
/// use writeable::assert_writeable_eq;
68
///
69
/// // Read an ICU4X data blob statically:
70
/// const HELLO_WORLD_BLOB: &[u8] = include_bytes!(concat!(
71
///     env!("CARGO_MANIFEST_DIR"),
72
///     "/tests/data/v2.postcard"
73
/// ));
74
///
75
/// // Create a DataProvider from it:
76
/// let provider = BlobDataProvider::try_new_from_static_blob(HELLO_WORLD_BLOB)
77
///     .expect("Deserialization should succeed");
78
///
79
/// // Check that it works:
80
/// let formatter = HelloWorldFormatter::try_new_with_buffer_provider(
81
///     &provider,
82
///     &locale!("la").into(),
83
/// )
84
/// .expect("locale exists");
85
///
86
/// assert_writeable_eq!(formatter.format(), "Ave, munde");
87
/// ```
88
#[derive(Clone)]
×
89
pub struct BlobDataProvider {
90
    data: Yoke<BlobSchema<'static>, Option<Cart>>,
×
91
}
92

93
impl core::fmt::Debug for BlobDataProvider {
94
    fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
×
95
        f.debug_struct("BlobDataProvider")
×
96
            .field("data", &"[...]")
97
            .finish()
98
    }
×
99
}
100

101
impl BlobDataProvider {
102
    /// Create a [`BlobDataProvider`] from a blob of ICU4X data.
103
    pub fn try_new_from_blob(blob: Box<[u8]>) -> Result<Self, DataError> {
8✔
104
        Ok(Self {
8✔
105
            data: Cart::try_make_yoke(blob, |bytes| {
16✔
106
                BlobSchema::deserialize_and_check(&mut postcard::Deserializer::from_bytes(bytes))
8✔
107
            })?,
8✔
108
        })
109
    }
8✔
110

111
    /// Create a [`BlobDataProvider`] from a static blob. This is a special case of
112
    /// [`try_new_from_blob`](BlobDataProvider::try_new_from_blob) and is allocation-free.
113
    pub fn try_new_from_static_blob(blob: &'static [u8]) -> Result<Self, DataError> {
5✔
114
        Ok(Self {
5✔
115
            data: Yoke::new_owned(BlobSchema::deserialize_and_check(
5✔
116
                &mut postcard::Deserializer::from_bytes(blob),
5✔
117
            )?),
×
118
        })
119
    }
5✔
120
}
121

122
impl BufferProvider for BlobDataProvider {
123
    fn load_buffer(
84✔
124
        &self,
125
        key: DataKey,
126
        req: DataRequest,
127
    ) -> Result<DataResponse<BufferMarker>, DataError> {
128
        let mut metadata = DataResponseMetadata::default();
84✔
129
        metadata.buffer_format = Some(BufferFormat::Postcard1);
84✔
130
        Ok(DataResponse {
76✔
131
            metadata,
84✔
132
            payload: Some(DataPayload::from_yoked_buffer(
75✔
133
                self.data
84✔
134
                    .try_map_project_cloned(|blob, _| blob.load(key, req))?,
177✔
135
            )),
136
        })
8✔
137
    }
84✔
138
}
139

140
#[cfg(test)]
141
mod test {
142
    use super::*;
143
    use crate::export::*;
144
    use icu_provider::datagen::*;
145
    use icu_provider::hello_world::*;
146

147
    #[icu_provider::data_struct(marker(HelloSingletonV1Marker, "hello/singleton@1", singleton))]
×
148
    #[derive(Clone, Copy)]
×
149
    pub struct HelloSingletonV1;
150

151
    #[test]
152
    fn test_empty() {
2✔
153
        for version in [1, 2] {
3✔
154
            let mut blob: Vec<u8> = Vec::new();
2✔
155

156
            {
2✔
157
                let mut exporter = if version == 1 {
2✔
158
                    BlobExporter::new_with_sink(Box::new(&mut blob))
1✔
159
                } else {
160
                    BlobExporter::new_v2_with_sink(Box::new(&mut blob))
1✔
161
                };
162

163
                exporter.flush(HelloWorldV1Marker::KEY).unwrap();
2✔
164

165
                exporter.close().unwrap();
2✔
166
            }
2✔
167

168
            let provider = BlobDataProvider::try_new_from_blob(blob.into()).unwrap();
2✔
169

170
            assert!(
2✔
171
                matches!(
2✔
172
                    provider.load_buffer(HelloWorldV1Marker::KEY, Default::default()),
2✔
173
                    Err(DataError {
174
                        kind: DataErrorKind::MissingLocale,
175
                        ..
176
                    })
177
                ),
178
                "(version: {version})"
179
            );
180
        }
2✔
181
    }
2✔
182

183
    #[test]
184
    fn test_singleton() {
2✔
185
        for version in [1, 2] {
3✔
186
            let mut blob: Vec<u8> = Vec::new();
2✔
187

188
            {
2✔
189
                let mut exporter = if version == 1 {
2✔
190
                    BlobExporter::new_with_sink(Box::new(&mut blob))
1✔
191
                } else {
192
                    BlobExporter::new_v2_with_sink(Box::new(&mut blob))
1✔
193
                };
194

195
                exporter.flush(HelloSingletonV1Marker::KEY).unwrap();
2✔
196

197
                exporter.close().unwrap();
2✔
198
            }
2✔
199

200
            let provider = BlobDataProvider::try_new_from_blob(blob.into()).unwrap();
2✔
201

202
            assert!(
2✔
203
                matches!(
2✔
204
                    provider.load_buffer(
2✔
205
                        HelloSingletonV1Marker::KEY,
206
                        DataRequest {
2✔
207
                            locale: &icu_locid::locale!("de").into(),
2✔
208
                            metadata: Default::default()
2✔
209
                        }
210
                    ),
211
                    Err(DataError {
212
                        kind: DataErrorKind::ExtraneousLocale,
213
                        ..
214
                    })
215
                ),
216
                "(version: {version})"
217
            );
218

219
            assert!(
2✔
220
                matches!(
2✔
221
                    provider.load_buffer(HelloSingletonV1Marker::KEY, Default::default()),
2✔
222
                    Err(DataError {
223
                        kind: DataErrorKind::MissingLocale,
224
                        ..
225
                    })
226
                ),
227
                "(version: {version})"
228
            );
229
        }
2✔
230
    }
2✔
231
}
STATUS · Troubleshooting · Open an Issue · Sales · Support · CAREERS · ENTERPRISE · START FREE · SCHEDULE DEMO
ANNOUNCEMENTS · TWITTER · TOS & SLA · Supported CI Services · What's a CI service? · Automated Testing

© 2025 Coveralls, Inc