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                value_written: false,
334            }
335        };
336        Ok(Compound {
337            ser: self,
338            first: true,
339            mode,
340        })
341    }
342
343    fn serialize_struct_variant(
344        self,
345        _: &'static str,
346        _: u32,
347        var: &'static str,
348        _: usize,
349    ) -> Result<Self::SerializeStructVariant, Self::Error> {
350        self.output.push(b'{');
351        self.write_string(var);
352        self.output.extend_from_slice(b"={");
353        Ok(Compound {
354            ser: self,
355            first: true,
356            mode: CompoundMode::VariantMap,
357        })
358    }
359}
360
361#[derive(Clone, Copy)]
362enum CompoundMode {
363    Seq,
364    Map,
365    Attr,
366    AttrWrapper,
367    VariantSeq,
368    VariantMap,
369    Struct {
370        attr_open: bool,
371        body_open: bool,
372        /// A `$value` field has been written: the struct *is* that value, and
373        /// no further field can follow it.
374        value_written: bool,
375    },
376}
377
378/// A helper for serializing compound YSON types such as lists, maps, and structs.
379pub struct Compound<'a> {
380    ser: &'a mut Serializer,
381    first: bool,
382    mode: CompoundMode,
383}
384
385impl Compound<'_> {
386    #[inline]
387    fn check_first(&mut self) {
388        if !self.first {
389            self.ser.output.push(b';');
390        }
391        self.first = false;
392    }
393}
394
395macro_rules! delegate_seq {
396    ($($trait:ident),*) => {
397        $(impl<'a> ser::$trait for Compound<'a> {
398            type Ok = (); type Error = YsonError;
399            fn serialize_element<T: ?Sized + Serialize>(&mut self, v: &T) -> Result<(), Self::Error> {
400                self.check_first(); v.serialize(&mut *self.ser)
401            }
402            fn end(self) -> Result<(), Self::Error> { self.ser.output.push(b']'); Ok(()) }
403        })*
404    };
405}
406delegate_seq!(SerializeSeq, SerializeTuple);
407
408impl ser::SerializeTupleStruct for Compound<'_> {
409    type Ok = ();
410    type Error = YsonError;
411    fn serialize_field<T: ?Sized + Serialize>(&mut self, v: &T) -> Result<(), Self::Error> {
412        self.check_first();
413        v.serialize(&mut *self.ser)
414    }
415    fn end(self) -> Result<(), Self::Error> {
416        self.ser.output.push(b']');
417        Ok(())
418    }
419}
420
421impl ser::SerializeTupleVariant for Compound<'_> {
422    type Ok = ();
423    type Error = YsonError;
424    fn serialize_field<T: ?Sized + Serialize>(&mut self, v: &T) -> Result<(), Self::Error> {
425        self.check_first();
426        v.serialize(&mut *self.ser)
427    }
428    fn end(self) -> Result<(), Self::Error> {
429        self.ser.output.extend_from_slice(b"]}");
430        Ok(())
431    }
432}
433
434impl ser::SerializeMap for Compound<'_> {
435    type Ok = ();
436    type Error = YsonError;
437    fn serialize_key<T: ?Sized + Serialize>(&mut self, key: &T) -> Result<(), Self::Error> {
438        self.check_first();
439        key.serialize(&mut *self.ser)
440    }
441    fn serialize_value<T: ?Sized + Serialize>(&mut self, value: &T) -> Result<(), Self::Error> {
442        self.ser.output.push(b'=');
443        value.serialize(&mut *self.ser)
444    }
445    fn end(self) -> Result<(), Self::Error> {
446        self.ser
447            .output
448            .push(if matches!(self.mode, CompoundMode::Attr) {
449                b'>'
450            } else {
451                b'}'
452            });
453        Ok(())
454    }
455}
456
457impl ser::SerializeStruct for Compound<'_> {
458    type Ok = ();
459    type Error = YsonError;
460
461    fn serialize_field<T: ?Sized + Serialize>(
462        &mut self,
463        key: &'static str,
464        value: &T,
465    ) -> Result<(), Self::Error> {
466        match self.mode {
467            CompoundMode::AttrWrapper => {
468                if key == "$attributes" {
469                    self.ser.is_writing_attributes = true;
470                    value.serialize(&mut *self.ser)?;
471                } else if key == "$value" {
472                    value.serialize(&mut *self.ser)?;
473                }
474            }
475            CompoundMode::Struct {
476                mut attr_open,
477                mut body_open,
478                mut value_written,
479            } => {
480                if let Some(attr_name) = key.strip_prefix('@') {
481                    // YSON attributes stand strictly before the value they
482                    // decorate. Opening `<` here would land it inside the map
483                    // body, and the output would not parse.
484                    if body_open || value_written {
485                        return Err(YsonError::Custom(format!(
486                            "attribute field \"@{attr_name}\" is declared after a value \
487                             field; attribute fields must come first in the struct"
488                        )));
489                    }
490                    if !attr_open {
491                        self.ser.output.push(b'<');
492                        attr_open = true;
493                        self.first = true;
494                    }
495                    self.check_first();
496                    self.ser.write_string(attr_name);
497                    self.ser.output.push(b'=');
498                } else {
499                    if attr_open {
500                        self.ser.output.push(b'>');
501                        attr_open = false;
502                    }
503                    if key == "$value" {
504                        // The struct *is* this value; it cannot also have map
505                        // entries, before or after.
506                        if body_open || value_written {
507                            return Err(YsonError::Custom(
508                                "\"$value\" cannot share a struct with plain fields: \
509                                 one value cannot have two bodies"
510                                    .into(),
511                            ));
512                        }
513                        value_written = true;
514                    } else {
515                        if value_written {
516                            return Err(YsonError::Custom(format!(
517                                "field \"{key}\" is declared after \"$value\"; a struct \
518                                 with a \"$value\" field can carry only attributes beside it"
519                            )));
520                        }
521                        if !body_open {
522                            self.ser.output.push(b'{');
523                            body_open = true;
524                            self.first = true;
525                        }
526                        self.check_first();
527                        self.ser.write_string(key);
528                        self.ser.output.push(b'=');
529                    }
530                }
531
532                self.mode = CompoundMode::Struct {
533                    attr_open,
534                    body_open,
535                    value_written,
536                };
537                value.serialize(&mut *self.ser)?;
538            }
539            _ => {
540                self.check_first();
541                self.ser.write_string(key);
542                self.ser.output.push(b'=');
543                value.serialize(&mut *self.ser)?;
544            }
545        }
546        Ok(())
547    }
548
549    fn end(self) -> Result<(), Self::Error> {
550        match self.mode {
551            CompoundMode::Attr => self.ser.output.push(b'>'),
552            CompoundMode::Seq | CompoundMode::VariantSeq => self.ser.output.push(b']'),
553            CompoundMode::Struct {
554                attr_open,
555                body_open,
556                value_written,
557            } => {
558                if attr_open {
559                    // Every field was an attribute. An attribute block cannot
560                    // stand alone, and the only value an all-attribute struct
561                    // can mean is the entity: `<a=1>#`.
562                    self.ser.output.extend_from_slice(b">#");
563                } else if body_open {
564                    self.ser.output.push(b'}');
565                } else if !value_written {
566                    // No fields at all. Zero bytes is not a YSON value; an
567                    // empty struct is an empty map.
568                    self.ser.output.extend_from_slice(b"{}");
569                }
570            }
571            CompoundMode::AttrWrapper => {}
572            _ => self.ser.output.push(b'}'),
573        }
574        Ok(())
575    }
576}
577
578impl ser::SerializeStructVariant for Compound<'_> {
579    type Ok = ();
580    type Error = YsonError;
581    fn serialize_field<T: ?Sized + Serialize>(
582        &mut self,
583        k: &'static str,
584        v: &T,
585    ) -> Result<(), Self::Error> {
586        ser::SerializeStruct::serialize_field(self, k, v)
587    }
588    fn end(self) -> Result<(), Self::Error> {
589        self.ser.output.extend_from_slice(b"}}");
590        Ok(())
591    }
592}
593
594fn is_safe_unquoted(b: &[u8]) -> bool {
595    matches!(b.first(), Some(f) if f.is_ascii_alphabetic() || *f == b'_')
596        && b.iter()
597            .all(|&c| c.is_ascii_alphanumeric() || b"_-.".contains(&c))
598}