Skip to main content

ytsaurus_yson/
ser.rs

1use crate::error::YsonError;
2use serde::{Serialize, ser};
3
4/// Supported format for YSON data representation
5#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
6pub enum YsonFormat {
7    /// Binary format
8    Binary,
9    /// Text format (human readable)
10    Text,
11}
12
13/// A structure for serializing Rust types into YSON byte sequences.
14pub struct Serializer {
15    /// The buffer where the serialized YSON bytes are stored.
16    pub output: Vec<u8>,
17    pub(crate) is_binary: bool,
18    pub(crate) is_writing_attributes: bool,
19}
20
21impl Serializer {
22    /// Creates a new `Serializer` instance with a pre-allocated buffer.
23    ///
24    /// # Arguments
25    ///
26    /// * `is_binary` - Set to `true` for binary output, or `false` for text output.
27    ///
28    /// # Examples
29    ///
30    /// ```
31    /// use ytsaurus_yson::ser::Serializer;
32    /// use serde::Serialize;
33    ///
34    /// let mut ser = Serializer::new(false);
35    /// 42i64.serialize(&mut ser).unwrap();
36    ///
37    /// assert_eq!(ser.output, b"42");
38    /// ```
39    #[must_use]
40    pub fn new(is_binary: bool) -> Self {
41        Self::with_buffer(Vec::with_capacity(8192), is_binary)
42    }
43
44    /// Creates a serializer that appends into an existing buffer.
45    ///
46    /// Lets a caller that serializes many values in a row — a job writing one
47    /// row per record, say — reuse a single allocation instead of paying for a
48    /// fresh `Vec` each time. The buffer is **not** cleared; clear it yourself
49    /// between values if you want them separately.
50    #[must_use]
51    pub fn with_buffer(buffer: Vec<u8>, is_binary: bool) -> Self {
52        Self {
53            output: buffer,
54            is_binary,
55            is_writing_attributes: false,
56        }
57    }
58
59    /// Consumes the serializer and returns the buffer, ready to be reused.
60    #[must_use]
61    pub fn into_output(self) -> Vec<u8> {
62        self.output
63    }
64
65    #[inline]
66    fn write_entity(&mut self) {
67        self.output.push(0x23);
68    }
69
70    fn write_bool(&mut self, v: bool) {
71        if self.is_binary {
72            self.output.push(if v { 0x05 } else { 0x04 });
73        } else {
74            self.output
75                .extend_from_slice(if v { b"%true" } else { b"%false" });
76        }
77    }
78
79    fn write_i64(&mut self, v: i64) {
80        if self.is_binary {
81            self.output.push(0x02);
82            crate::varint::write_varint(v, &mut self.output);
83        } else {
84            self.output
85                .extend_from_slice(itoa::Buffer::new().format(v).as_bytes());
86        }
87    }
88
89    fn write_u64(&mut self, v: u64) {
90        if self.is_binary {
91            self.output.push(0x06);
92            crate::varint::write_uvarint(v, &mut self.output);
93        } else {
94            self.output
95                .extend_from_slice(itoa::Buffer::new().format(v).as_bytes());
96            self.output.push(b'u');
97        }
98    }
99
100    fn write_f64(&mut self, v: f64) {
101        if self.is_binary {
102            self.output.push(0x03);
103            self.output.extend_from_slice(&v.to_le_bytes());
104        } else if v.is_nan() {
105            self.output.extend_from_slice(b"%nan");
106        } else if v.is_infinite() {
107            self.output.extend_from_slice(if v.is_sign_negative() {
108                b"%-inf"
109            } else {
110                b"%inf"
111            });
112        } else {
113            let s = ryu::Buffer::new().format(v).to_owned();
114            self.output.extend_from_slice(s.as_bytes());
115            if !s.contains(&['.', 'e', 'E'][..]) {
116                self.output.extend_from_slice(b".0");
117            }
118        }
119    }
120
121    fn write_string(&mut self, v: &str) {
122        if self.is_binary {
123            self.output.push(0x01);
124            crate::varint::write_varint(v.len() as i64, &mut self.output);
125            self.output.extend_from_slice(v.as_bytes());
126        } else if is_safe_unquoted(v.as_bytes()) {
127            self.output.extend_from_slice(v.as_bytes());
128        } else {
129            self.output.push(b'"');
130            for &b in v.as_bytes() {
131                match b {
132                    b'"' => self.output.extend_from_slice(b"\\\""),
133                    b'\\' => self.output.extend_from_slice(b"\\\\"),
134                    b'\n' => self.output.extend_from_slice(b"\\n"),
135                    b'\r' => self.output.extend_from_slice(b"\\r"),
136                    b'\t' => self.output.extend_from_slice(b"\\t"),
137                    0x00..=0x1F => {
138                        const HEX: &[u8] = b"0123456789abcdef";
139                        self.output.extend_from_slice(&[
140                            b'\\',
141                            b'x',
142                            HEX[(b >> 4) as usize],
143                            HEX[(b & 0x0F) as usize],
144                        ]);
145                    }
146                    _ => self.output.push(b),
147                }
148            }
149            self.output.push(b'"');
150        }
151    }
152}
153
154macro_rules! impl_serialize {
155    // Numbers
156    ($($name:ident($ty:ty) => $method:ident as $cast:ty),*) => {
157        $(fn $name(self, v: $ty) -> Result<(), Self::Error> { self.$method(v as $cast); Ok(()) })*
158    };
159    // None, Unit
160    (@empty $($name:ident $(($($arg:ident: $ty:ty),*))?),*) => {
161        $(fn $name(self $(, $($arg: $ty),*)?) -> Result<(), Self::Error> { self.write_entity(); Ok(()) })*
162    };
163}
164
165impl<'a> ser::Serializer for &'a mut Serializer {
166    type Ok = ();
167    type Error = YsonError;
168    type SerializeSeq = Compound<'a>;
169    type SerializeTuple = Compound<'a>;
170    type SerializeTupleStruct = Compound<'a>;
171    type SerializeTupleVariant = Compound<'a>;
172    type SerializeMap = Compound<'a>;
173    type SerializeStruct = Compound<'a>;
174    type SerializeStructVariant = Compound<'a>;
175
176    impl_serialize! {
177        serialize_i8(i8) => write_i64 as i64, serialize_i16(i16) => write_i64 as i64,
178        serialize_i32(i32) => write_i64 as i64, serialize_i64(i64) => write_i64 as i64,
179        serialize_u8(u8) => write_u64 as u64, serialize_u16(u16) => write_u64 as u64,
180        serialize_u32(u32) => write_u64 as u64, serialize_u64(u64) => write_u64 as u64,
181        serialize_f32(f32) => write_f64 as f64, serialize_f64(f64) => write_f64 as f64
182    }
183
184    impl_serialize!(@empty serialize_none, serialize_unit, serialize_unit_struct(_n: &'static str));
185
186    fn serialize_bool(self, v: bool) -> Result<(), Self::Error> {
187        self.write_bool(v);
188        Ok(())
189    }
190    fn serialize_char(self, v: char) -> Result<(), Self::Error> {
191        self.write_string(&v.to_string());
192        Ok(())
193    }
194    fn serialize_str(self, v: &str) -> Result<(), Self::Error> {
195        self.write_string(v);
196        Ok(())
197    }
198
199    fn serialize_bytes(self, v: &[u8]) -> Result<(), Self::Error> {
200        if self.is_binary {
201            self.output.push(0x01);
202            crate::varint::write_varint(v.len() as i64, &mut self.output);
203            self.output.extend_from_slice(v);
204        } else {
205            self.output.push(b'"');
206            for &b in v {
207                match b {
208                    b'"' => self.output.extend_from_slice(b"\\\""),
209                    b'\\' => self.output.extend_from_slice(b"\\\\"),
210                    b'\n' => self.output.extend_from_slice(b"\\n"),
211                    b'\r' => self.output.extend_from_slice(b"\\r"),
212                    b'\t' => self.output.extend_from_slice(b"\\t"),
213                    0x20..=0x7E => self.output.push(b),
214                    _ => {
215                        const HEX: &[u8] = b"0123456789abcdef";
216                        self.output.extend_from_slice(&[
217                            b'\\',
218                            b'x',
219                            HEX[(b >> 4) as usize],
220                            HEX[(b & 0x0F) as usize],
221                        ]);
222                    }
223                }
224            }
225            self.output.push(b'"');
226        }
227        Ok(())
228    }
229
230    fn serialize_some<T: ?Sized + Serialize>(self, v: &T) -> Result<(), Self::Error> {
231        v.serialize(self)
232    }
233    fn serialize_newtype_struct<T: ?Sized + Serialize>(
234        self,
235        _: &'static str,
236        v: &T,
237    ) -> Result<(), Self::Error> {
238        v.serialize(self)
239    }
240
241    fn serialize_unit_variant(
242        self,
243        _: &'static str,
244        _: u32,
245        variant: &'static str,
246    ) -> Result<(), Self::Error> {
247        self.write_string(variant);
248        Ok(())
249    }
250
251    fn serialize_newtype_variant<T: ?Sized + Serialize>(
252        self,
253        _: &'static str,
254        _: u32,
255        var: &'static str,
256        val: &T,
257    ) -> Result<(), Self::Error> {
258        self.output.push(b'{');
259        self.write_string(var);
260        self.output.push(b'=');
261        val.serialize(&mut *self)?;
262        self.output.push(b'}');
263        Ok(())
264    }
265
266    fn serialize_seq(self, _: Option<usize>) -> Result<Self::SerializeSeq, Self::Error> {
267        self.output.push(b'[');
268        Ok(Compound {
269            ser: self,
270            first: true,
271            mode: CompoundMode::Seq,
272        })
273    }
274
275    fn serialize_tuple(self, len: usize) -> Result<Self::SerializeTuple, Self::Error> {
276        self.serialize_seq(Some(len))
277    }
278    fn serialize_tuple_struct(
279        self,
280        _: &'static str,
281        len: usize,
282    ) -> Result<Self::SerializeTupleStruct, Self::Error> {
283        self.serialize_seq(Some(len))
284    }
285
286    fn serialize_tuple_variant(
287        self,
288        _: &'static str,
289        _: u32,
290        var: &'static str,
291        _: usize,
292    ) -> Result<Self::SerializeTupleVariant, Self::Error> {
293        self.output.push(b'{');
294        self.write_string(var);
295        self.output.extend_from_slice(b"=[");
296        Ok(Compound {
297            ser: self,
298            first: true,
299            mode: CompoundMode::VariantSeq,
300        })
301    }
302
303    fn serialize_map(self, _: Option<usize>) -> Result<Self::SerializeMap, Self::Error> {
304        let (open, mode) = if self.is_writing_attributes {
305            (b'<', CompoundMode::Attr)
306        } else {
307            (b'{', CompoundMode::Map)
308        };
309        self.output.push(open);
310        self.is_writing_attributes = false;
311        Ok(Compound {
312            ser: self,
313            first: true,
314            mode,
315        })
316    }
317
318    fn serialize_struct(
319        self,
320        name: &'static str,
321        _: usize,
322    ) -> Result<Self::SerializeStruct, Self::Error> {
323        let mode = if name == "$__yson_attributes" {
324            CompoundMode::AttrWrapper
325        } else if self.is_writing_attributes {
326            self.output.push(b'<');
327            self.is_writing_attributes = false;
328            CompoundMode::Attr
329        } else {
330            CompoundMode::Struct {
331                attr_open: false,
332                body_open: false,
333            }
334        };
335        Ok(Compound {
336            ser: self,
337            first: true,
338            mode,
339        })
340    }
341
342    fn serialize_struct_variant(
343        self,
344        _: &'static str,
345        _: u32,
346        var: &'static str,
347        _: usize,
348    ) -> Result<Self::SerializeStructVariant, Self::Error> {
349        self.output.push(b'{');
350        self.write_string(var);
351        self.output.extend_from_slice(b"={");
352        Ok(Compound {
353            ser: self,
354            first: true,
355            mode: CompoundMode::VariantMap,
356        })
357    }
358}
359
360#[derive(Clone, Copy)]
361enum CompoundMode {
362    Seq,
363    Map,
364    Attr,
365    AttrWrapper,
366    VariantSeq,
367    VariantMap,
368    Struct { attr_open: bool, body_open: bool },
369}
370
371/// A helper for serializing compound YSON types such as lists, maps, and structs.
372pub struct Compound<'a> {
373    ser: &'a mut Serializer,
374    first: bool,
375    mode: CompoundMode,
376}
377
378impl Compound<'_> {
379    #[inline]
380    fn check_first(&mut self) {
381        if !self.first {
382            self.ser.output.push(b';');
383        }
384        self.first = false;
385    }
386}
387
388macro_rules! delegate_seq {
389    ($($trait:ident),*) => {
390        $(impl<'a> ser::$trait for Compound<'a> {
391            type Ok = (); type Error = YsonError;
392            fn serialize_element<T: ?Sized + Serialize>(&mut self, v: &T) -> Result<(), Self::Error> {
393                self.check_first(); v.serialize(&mut *self.ser)
394            }
395            fn end(self) -> Result<(), Self::Error> { self.ser.output.push(b']'); Ok(()) }
396        })*
397    };
398}
399delegate_seq!(SerializeSeq, SerializeTuple);
400
401impl ser::SerializeTupleStruct for Compound<'_> {
402    type Ok = ();
403    type Error = YsonError;
404    fn serialize_field<T: ?Sized + Serialize>(&mut self, v: &T) -> Result<(), Self::Error> {
405        self.check_first();
406        v.serialize(&mut *self.ser)
407    }
408    fn end(self) -> Result<(), Self::Error> {
409        self.ser.output.push(b']');
410        Ok(())
411    }
412}
413
414impl ser::SerializeTupleVariant for Compound<'_> {
415    type Ok = ();
416    type Error = YsonError;
417    fn serialize_field<T: ?Sized + Serialize>(&mut self, v: &T) -> Result<(), Self::Error> {
418        self.check_first();
419        v.serialize(&mut *self.ser)
420    }
421    fn end(self) -> Result<(), Self::Error> {
422        self.ser.output.extend_from_slice(b"]}");
423        Ok(())
424    }
425}
426
427impl ser::SerializeMap for Compound<'_> {
428    type Ok = ();
429    type Error = YsonError;
430    fn serialize_key<T: ?Sized + Serialize>(&mut self, key: &T) -> Result<(), Self::Error> {
431        self.check_first();
432        key.serialize(&mut *self.ser)
433    }
434    fn serialize_value<T: ?Sized + Serialize>(&mut self, value: &T) -> Result<(), Self::Error> {
435        self.ser.output.push(b'=');
436        value.serialize(&mut *self.ser)
437    }
438    fn end(self) -> Result<(), Self::Error> {
439        self.ser
440            .output
441            .push(if matches!(self.mode, CompoundMode::Attr) {
442                b'>'
443            } else {
444                b'}'
445            });
446        Ok(())
447    }
448}
449
450impl ser::SerializeStruct for Compound<'_> {
451    type Ok = ();
452    type Error = YsonError;
453
454    fn serialize_field<T: ?Sized + Serialize>(
455        &mut self,
456        key: &'static str,
457        value: &T,
458    ) -> Result<(), Self::Error> {
459        match self.mode {
460            CompoundMode::AttrWrapper => {
461                if key == "$attributes" {
462                    self.ser.is_writing_attributes = true;
463                    value.serialize(&mut *self.ser)?;
464                } else if key == "$value" {
465                    value.serialize(&mut *self.ser)?;
466                }
467            }
468            CompoundMode::Struct {
469                mut attr_open,
470                mut body_open,
471            } => {
472                if let Some(attr_name) = key.strip_prefix('@') {
473                    if !attr_open {
474                        self.ser.output.push(b'<');
475                        attr_open = true;
476                        self.first = true;
477                    }
478                    self.check_first();
479                    self.ser.write_string(attr_name);
480                    self.ser.output.push(b'=');
481                } else {
482                    if attr_open {
483                        self.ser.output.push(b'>');
484                        attr_open = false;
485                    }
486                    if key != "$value" {
487                        if !body_open {
488                            self.ser.output.push(b'{');
489                            body_open = true;
490                            self.first = true;
491                        }
492                        self.check_first();
493                        self.ser.write_string(key);
494                        self.ser.output.push(b'=');
495                    }
496                }
497
498                self.mode = CompoundMode::Struct {
499                    attr_open,
500                    body_open,
501                };
502                value.serialize(&mut *self.ser)?;
503            }
504            _ => {
505                self.check_first();
506                self.ser.write_string(key);
507                self.ser.output.push(b'=');
508                value.serialize(&mut *self.ser)?;
509            }
510        }
511        Ok(())
512    }
513
514    fn end(self) -> Result<(), Self::Error> {
515        match self.mode {
516            CompoundMode::Attr => self.ser.output.push(b'>'),
517            CompoundMode::Seq | CompoundMode::VariantSeq => self.ser.output.push(b']'),
518            CompoundMode::Struct {
519                attr_open,
520                body_open,
521            } => {
522                if attr_open {
523                    self.ser.output.push(b'>');
524                }
525                if body_open {
526                    self.ser.output.push(b'}');
527                }
528            }
529            CompoundMode::AttrWrapper => {}
530            _ => self.ser.output.push(b'}'),
531        }
532        Ok(())
533    }
534}
535
536impl ser::SerializeStructVariant for Compound<'_> {
537    type Ok = ();
538    type Error = YsonError;
539    fn serialize_field<T: ?Sized + Serialize>(
540        &mut self,
541        k: &'static str,
542        v: &T,
543    ) -> Result<(), Self::Error> {
544        ser::SerializeStruct::serialize_field(self, k, v)
545    }
546    fn end(self) -> Result<(), Self::Error> {
547        self.ser.output.extend_from_slice(b"}}");
548        Ok(())
549    }
550}
551
552fn is_safe_unquoted(b: &[u8]) -> bool {
553    matches!(b.first(), Some(f) if f.is_ascii_alphabetic() || *f == b'_')
554        && b.iter()
555            .all(|&c| c.is_ascii_alphanumeric() || b"_-.".contains(&c))
556}