compact_thrift_runtime/
types.rs

1use std::borrow::Cow;
2use std::rc::Rc;
3use std::sync::Arc;
4use crate::protocol::*;
5use crate::ThriftError;
6
7/// For structs, the following field-types can be encoded:
8///
9///  - BOOLEAN_TRUE, encoded as 1
10///  - BOOLEAN_FALSE, encoded as 2
11///
12/// For lists and sets the following element-types are used (see note 1 below):
13///
14///  - BOOL, encoded as 1 or 2 (see note below)
15///
16/// The only valid value in the original spec was 2, but due to an widespread implementation
17/// bug the defacto standard across large parts of the library became 1 instead.
18/// As a result, both values are now allowed.
19///
20/// Element values of type bool are sent as an int8; true as 1 and false as 2.
21///
22/// Previous versions of the spec said true is 1 and false is 0,
23/// so we are a bit more lenient and support both when reading.
24impl <'i> CompactThriftProtocol<'i> for bool {
25    const FIELD_TYPE: u8 = 2; // TRUE = 1, FALSE = 2
26
27    #[inline]
28    fn fill_thrift<T: CompactThriftInput<'i>>(&mut self, input: &mut T) -> Result<(), ThriftError> {
29        let byte = input.read_byte()?;
30        *self = byte == 1;
31        Ok(())
32    }
33
34    #[inline]
35    fn fill_thrift_field<T: CompactThriftInput<'i>>(&mut self, _input: &mut T, field_type: u8) -> Result<(), ThriftError> {
36        *self = field_type == 1;
37        Ok(())
38    }
39
40    #[inline]
41    fn write_thrift<T: CompactThriftOutput>(&self, output: &mut T) -> Result<(), ThriftError> {
42        let value = 1 + (!*self) as u8;
43        output.write_byte(value)
44    }
45
46    fn write_thrift_field<T: CompactThriftOutput>(&self, output: &mut T, field_id: i16, last_field_id: &mut i16) -> Result<(), ThriftError> {
47        let field_type = 1 + (!*self) as u8;
48        write_field_header(output, field_type, field_id, last_field_id)
49    }
50}
51
52impl <'i> CompactThriftProtocol<'i> for i8 {
53    const FIELD_TYPE: u8 = 3;
54
55    #[inline]
56    fn fill_thrift<T: CompactThriftInput<'i>>(&mut self, input: &mut T) -> Result<(), ThriftError> {
57        *self = input.read_byte()? as i8;
58        Ok(())
59    }
60
61    #[inline]
62    fn write_thrift<T: CompactThriftOutput>(&self, output: &mut T) -> Result<(), ThriftError> {
63        output.write_byte(*self as u8)
64    }
65}
66
67impl <'i> CompactThriftProtocol<'i> for i16 {
68    const FIELD_TYPE: u8 = 4;
69
70    #[inline]
71    fn fill_thrift<T: CompactThriftInput<'i>>(&mut self, input: &mut T) -> Result<(), ThriftError> {
72        *self = input.read_i16()?;
73        Ok(())
74    }
75
76    #[inline]
77    fn write_thrift<T: CompactThriftOutput>(&self, output: &mut T) -> Result<(), ThriftError> {
78        output.write_i16(*self)
79    }
80}
81
82impl <'i> CompactThriftProtocol<'i> for i32 {
83    const FIELD_TYPE: u8 = 5;
84
85    #[inline]
86    fn fill_thrift<T: CompactThriftInput<'i>>(&mut self, input: &mut T) -> Result<(), ThriftError> {
87        *self = input.read_i32()?;
88        Ok(())
89    }
90
91    #[inline]
92    fn write_thrift<T: CompactThriftOutput>(&self, output: &mut T) -> Result<(), ThriftError> {
93        output.write_i32(*self)
94    }
95}
96
97impl <'i> CompactThriftProtocol<'i> for i64 {
98    const FIELD_TYPE: u8 = 6;
99
100    #[inline]
101    fn fill_thrift<T: CompactThriftInput<'i>>(&mut self, input: &mut T) -> Result<(), ThriftError> {
102        *self = input.read_i64()?;
103        Ok(())
104    }
105
106    #[inline]
107    fn write_thrift<T: CompactThriftOutput>(&self, output: &mut T) -> Result<(), ThriftError> {
108        output.write_i64(*self)
109    }
110}
111
112impl <'i> CompactThriftProtocol<'i> for f64 {
113    const FIELD_TYPE: u8 = 7;
114
115    #[inline]
116    fn fill_thrift<T: CompactThriftInput<'i>>(&mut self, input: &mut T) -> Result<(), ThriftError> {
117        *self = input.read_double()?;
118        Ok(())
119    }
120
121    #[inline]
122    fn write_thrift<T: CompactThriftOutput>(&self, output: &mut T) -> Result<(), ThriftError> {
123        output.write_double(*self)
124    }
125}
126
127impl <'i> CompactThriftProtocol<'i> for Vec<u8> {
128    const FIELD_TYPE: u8 = 8;
129
130    #[inline]
131    fn fill_thrift<T: CompactThriftInput<'i>>(&mut self, input: &mut T) -> Result<(), ThriftError> {
132        *self = input.read_binary()?.into_owned();
133        Ok(())
134    }
135
136    #[inline]
137    fn write_thrift<T: CompactThriftOutput>(&self, output: &mut T) -> Result<(), ThriftError> {
138        output.write_binary(self.as_slice())
139    }
140}
141
142impl <'i> CompactThriftProtocol<'i> for String {
143    // Same type as for Binary.
144    const FIELD_TYPE: u8 = 8;
145
146    #[inline]
147    fn fill_thrift<T: CompactThriftInput<'i>>(&mut self, input: &mut T) -> Result<(), ThriftError> {
148        *self = input.read_string()?.into_owned();
149        Ok(())
150    }
151
152    #[inline]
153    fn write_thrift<T: CompactThriftOutput>(&self, output: &mut T) -> Result<(), ThriftError> {
154        output.write_string(self.as_str())
155    }
156}
157
158impl <'i> CompactThriftProtocol<'i> for Cow<'i, [u8]> {
159    const FIELD_TYPE: u8 = 8;
160
161    #[inline]
162    fn fill_thrift<T: CompactThriftInput<'i>>(&mut self, input: &mut T) -> Result<(), ThriftError> {
163        *self = input.read_binary()?;
164        Ok(())
165    }
166
167    #[inline]
168    fn write_thrift<T: CompactThriftOutput>(&self, output: &mut T) -> Result<(), ThriftError> {
169        output.write_binary(self.as_ref())
170    }
171}
172
173impl <'i> CompactThriftProtocol<'i> for Cow<'i, str> {
174    const FIELD_TYPE: u8 = 8;
175
176    #[inline]
177    fn fill_thrift<T: CompactThriftInput<'i>>(&mut self, input: &mut T) -> Result<(), ThriftError> {
178        *self = input.read_string()?;
179        Ok(())
180    }
181
182    #[inline]
183    fn write_thrift<T: CompactThriftOutput>(&self, output: &mut T) -> Result<(), ThriftError> {
184        output.write_string(self.as_ref())
185    }
186}
187
188impl <'i, P: CompactThriftProtocol<'i> + Default> CompactThriftProtocol<'i> for Vec<P> {
189    const FIELD_TYPE: u8 = 9;
190
191    fn fill_thrift<T: CompactThriftInput<'i>>(&mut self, input: &mut T) -> Result<(), ThriftError> {
192        let (len, element_type) = read_collection_len_and_type(input)?;
193        // Special case for boolean elements:
194        // The only valid value in the original spec was 2, but due to a widespread implementation
195        // bug the defacto standard across large parts of the library became 1 instead.
196        if element_type != P::FIELD_TYPE && !(P::FIELD_TYPE == bool::FIELD_TYPE && element_type == 1) {
197            return Err(ThriftError::InvalidType);
198        }
199        self.clear();
200        self.try_reserve(len as usize).map_err(|_| ThriftError::ReserveError)?;
201
202        for _ in 0..len as usize {
203            // workaround for unnecessary memcpy calls when using Vec::push(P::default()) with larger structs
204            // https://github.com/rust-lang/rust/issues/125632
205            self.extend((0..1).map(|_| P::default()));
206            // Safety: we just pushed an element
207            unsafe {
208                self.last_mut().unwrap_unchecked().fill_thrift(input)?;
209            }
210        }
211
212        Ok(())
213    }
214
215    fn write_thrift<T: CompactThriftOutput>(&self, output: &mut T) -> Result<(), ThriftError> {
216        let len = self.len();
217        if len > MAX_COLLECTION_LEN {
218            return Err(ThriftError::InvalidCollectionLen);
219        }
220
221        if len < 15 {
222            let header = P::FIELD_TYPE | ((len as u8) << 4);
223            output.write_byte(header)?;
224        } else {
225            output.write_byte(P::FIELD_TYPE | 0xF0)?;
226            output.write_len(len)?;
227        }
228        self.iter().try_for_each(|value| {
229            value.write_thrift(output)
230        })
231    }
232}
233
234impl <'i, P: CompactThriftProtocol<'i> + Default> CompactThriftProtocol<'i> for Option<P> {
235    const FIELD_TYPE: u8 = P::FIELD_TYPE;
236
237    #[inline]
238    fn fill_thrift<T: CompactThriftInput<'i>>(&mut self, input: &mut T) -> Result<(), ThriftError> {
239        if self.is_some() {
240            return Err(ThriftError::DuplicateField);
241        }
242        // Safety: avoid generating drop calls, content is always None because of check above
243        unsafe {
244            std::ptr::write(self as *mut _, Some(P::default()));
245            self.as_mut().unwrap_unchecked().fill_thrift(input)?;
246        }
247        Ok(())
248    }
249
250    #[inline]
251    fn fill_thrift_field<T: CompactThriftInput<'i>>(&mut self, input: &mut T, field_type: u8) -> Result<(), ThriftError> {
252        if self.is_some() {
253            return Err(ThriftError::DuplicateField);
254        }
255        // Safety: avoid generating drop calls, content is always None because of check above
256        unsafe {
257            std::ptr::write(self as *mut _, Some(P::default()));
258            self.as_mut().unwrap_unchecked().fill_thrift_field(input, field_type)?;
259        }
260        Ok(())
261    }
262
263
264    #[inline]
265    fn write_thrift<T: CompactThriftOutput>(&self, output: &mut T) -> Result<(), ThriftError> {
266        if let Some(value) = self.as_ref() {
267            value.write_thrift(output)
268        } else {
269            Err(ThriftError::MissingValue)
270        }
271    }
272
273    #[inline]
274    fn write_thrift_field<T: CompactThriftOutput>(&self, output: &mut T, field_id: i16, last_field_id: &mut i16) -> Result<(), ThriftError> {
275        // only write if present
276        if let Some(value) = self.as_ref() {
277            value.write_thrift_field(output, field_id, last_field_id)?;
278        }
279        Ok(())
280    }
281}
282
283impl <'i, P: CompactThriftProtocol<'i> + Default> CompactThriftProtocol<'i> for Box<P> {
284    const FIELD_TYPE: u8 = P::FIELD_TYPE;
285
286    #[inline]
287    fn fill_thrift<T: CompactThriftInput<'i>>(&mut self, input: &mut T) -> Result<(), ThriftError> {
288        Box::as_mut(self).fill_thrift(input)
289    }
290
291    #[inline]
292    fn write_thrift<T: CompactThriftOutput>(&self, output: &mut T) -> Result<(), ThriftError> {
293        Box::as_ref(self).write_thrift(output)
294    }
295}
296
297impl <'i, P: CompactThriftProtocol<'i> + Default> CompactThriftProtocol<'i> for Rc<P> {
298    const FIELD_TYPE: u8 = P::FIELD_TYPE;
299
300    #[inline]
301    fn fill_thrift<T: CompactThriftInput<'i>>(&mut self, input: &mut T) -> Result<(), ThriftError> {
302        Rc::get_mut(self).ok_or(ThriftError::DuplicateField)?.fill_thrift(input)
303    }
304
305    #[inline]
306    fn write_thrift<T: CompactThriftOutput>(&self, output: &mut T) -> Result<(), ThriftError> {
307        Rc::as_ref(self).write_thrift(output)
308    }
309}
310
311impl <'i, P: CompactThriftProtocol<'i> + Default> CompactThriftProtocol<'i> for Arc<P> {
312    const FIELD_TYPE: u8 = P::FIELD_TYPE;
313
314    #[inline]
315    fn fill_thrift<T: CompactThriftInput<'i>>(&mut self, input: &mut T) -> Result<(), ThriftError> {
316        Arc::get_mut(self).ok_or(ThriftError::DuplicateField)?.fill_thrift(input)
317    }
318
319    #[inline]
320    fn write_thrift<T: CompactThriftOutput>(&self, output: &mut T) -> Result<(), ThriftError> {
321        Arc::as_ref(self).write_thrift(output)
322    }
323}
324
325impl <'i> CompactThriftProtocol<'i> for Box<str> {
326    const FIELD_TYPE: u8 = String::FIELD_TYPE;
327
328    #[inline]
329    fn fill_thrift<T: CompactThriftInput<'i>>(&mut self, input: &mut T) -> Result<(), ThriftError> {
330        *self = input.read_string()?.into_owned().into_boxed_str();
331        Ok(())
332    }
333
334    #[inline]
335    fn write_thrift<T: CompactThriftOutput>(&self, output: &mut T) -> Result<(), ThriftError> {
336        output.write_string(self.as_ref())
337    }
338}
339
340impl <'i> CompactThriftProtocol<'i> for Rc<str> {
341    const FIELD_TYPE: u8 = String::FIELD_TYPE;
342
343    #[inline]
344    fn fill_thrift<T: CompactThriftInput<'i>>(&mut self, input: &mut T) -> Result<(), ThriftError> {
345        *self = match input.read_string()? {
346            Cow::Borrowed(s) => Rc::from(s),
347            Cow::Owned(s) => Rc::from(s),
348        };
349        Ok(())
350    }
351
352    #[inline]
353    fn write_thrift<T: CompactThriftOutput>(&self, output: &mut T) -> Result<(), ThriftError> {
354        output.write_string(self.as_ref())
355    }
356}
357
358impl <'i> CompactThriftProtocol<'i> for Arc<str> {
359    const FIELD_TYPE: u8 = String::FIELD_TYPE;
360
361    #[inline]
362    fn fill_thrift<T: CompactThriftInput<'i>>(&mut self, input: &mut T) -> Result<(), ThriftError> {
363        *self = match input.read_string()? {
364            Cow::Borrowed(s) => Arc::from(s),
365            Cow::Owned(s) => Arc::from(s),
366        };
367        Ok(())
368    }
369
370    #[inline]
371    fn write_thrift<T: CompactThriftOutput>(&self, output: &mut T) -> Result<(), ThriftError> {
372        output.write_string(self.as_ref())
373    }
374}
375
376
377#[cfg(test)]
378mod tests {
379    use crate::{CompactThriftInputSlice, CompactThriftProtocol};
380
381    #[test]
382    fn test_encode_boolean_list() {
383        let mut output = vec![];
384        vec![false, true, true].write_thrift(&mut output).unwrap();
385        assert_eq!(&output, &[0b0011_0010, 2, 1, 1]);
386    }
387    #[test]
388    fn test_decode_boolean_list_tag1() {
389        let input = vec![0b0011_0001_u8, 2, 1, 0];
390        let values = Vec::<bool>::read_thrift(&mut CompactThriftInputSlice::new(&input)).unwrap();
391        assert_eq!(&values, &[false, true, false]);
392    }
393    #[test]
394    fn test_decode_boolean_list_tag2() {
395        let input = vec![0b0011_0010_u8, 2, 1, 0];
396        let values = Vec::<bool>::read_thrift(&mut CompactThriftInputSlice::new(&input)).unwrap();
397        assert_eq!(&values, &[false, true, false]);
398    }
399}