Skip to main content

deser_php/
ser.rs

1use alloc::borrow::Cow;
2use alloc::format;
3use alloc::string::{String, ToString};
4use alloc::vec::Vec;
5
6use deser_core::State;
7use deser_core::ext::{BigInt, ExtValue, Number};
8use deser_core::ser::{self, EventSink, SerializeDriver, SerializeRef};
9use deser_core::{Atom, Error, ErrorKind, Event, ImplicitValue, Serialize};
10
11use crate::float::{write_f32, write_f64};
12use crate::object::{ClassName, PropertyVisibility, Visibility};
13use crate::parser::{int_key, is_class_name, is_name};
14use crate::reference::{Reference, ReferenceKind};
15
16/// Configures how values are serialized.
17///
18/// The output is what PHP's `serialize` writes for the same values (see
19/// the [crate documentation](crate#serialization)).  The only option is
20/// the [`Context`](deser_core::Context) (see
21/// [`set_context`](Self::set_context)).
22///
23/// ```
24/// use deser_php::SerializerConfig;
25///
26/// const CONFIG: SerializerConfig = SerializerConfig::new();
27/// assert_eq!(CONFIG.to_vec(&vec![1, 2]).unwrap(), b"a:2:{i:0;i:1;i:1;i:2;}");
28/// ```
29#[derive(Debug, Clone, Default, PartialEq, Eq)]
30pub struct SerializerConfig {
31    context: deser_core::Context,
32}
33
34impl SerializerConfig {
35    /// Creates the default configuration.
36    pub const fn new() -> SerializerConfig {
37        SerializerConfig {
38            context: deser_core::Context::new(),
39        }
40    }
41
42    /// Returns a builder for the configuration (see [`SerializerConfigBuilder`]).
43    pub const fn builder() -> SerializerConfigBuilder {
44        SerializerConfigBuilder::new()
45    }
46
47    /// Returns a builder that starts with this configuration.
48    pub const fn into_builder(self) -> SerializerConfigBuilder {
49        SerializerConfigBuilder { value: self }
50    }
51
52    /// Sets the context the values are serialized in.
53    ///
54    /// The values of the context are the defaults of the extension values
55    /// of the state (see [`Context`](deser_core::Context)).  The
56    /// serializers and writers created with the configuration use this
57    /// context.  A context set on the driver takes precedence.
58    pub fn set_context(&mut self, context: deser_core::Context) {
59        self.context = context;
60    }
61
62    /// Returns the context the values are serialized in.
63    pub fn context(&self) -> &deser_core::Context {
64        &self.context
65    }
66
67    /// Gives the context to a driver which has none.
68    #[inline]
69    fn apply_context(&self, driver: &mut SerializeDriver<'_>) {
70        if !self.context.is_empty() {
71            driver.set_default_context(self.context.clone());
72        }
73    }
74
75    /// Serializes the given value.
76    pub fn to_vec<T: Serialize + ?Sized>(&self, value: &T) -> Result<Vec<u8>, Error> {
77        self.to_vec_ref(SerializeRef::new(&value))
78    }
79
80    /// Serializes the given value with a configured driver.
81    ///
82    /// The callback is invoked with the driver before the serialization
83    /// starts, for instance to add [`Layer`](deser_core::ser::Layer)s.
84    pub fn to_vec_with<F, T: Serialize + ?Sized>(
85        &self,
86        value: &T,
87        setup: F,
88    ) -> Result<Vec<u8>, Error>
89    where
90        F: FnOnce(&mut SerializeDriver<'_>),
91    {
92        let mut driver = SerializeDriver::new(&value);
93        setup(&mut driver);
94        self.apply_context(&mut driver);
95        serialize_driver(&mut driver)
96    }
97
98    /// Serializes a value whose type is erased (see
99    /// [`to_vec`](Self::to_vec)).
100    ///
101    /// This is not generic: the code that exists for every type only
102    /// erases it.
103    fn to_vec_ref(&self, value: SerializeRef<'_>) -> Result<Vec<u8>, Error> {
104        let mut driver = SerializeDriver::from_ref(value);
105        self.apply_context(&mut driver);
106        serialize_driver(&mut driver)
107    }
108}
109
110/// Builds a [`SerializerConfig`].
111///
112/// The methods have the names of the setters of [`SerializerConfig`] (without `set_`).
113#[derive(Debug, Clone)]
114#[must_use]
115pub struct SerializerConfigBuilder {
116    value: SerializerConfig,
117}
118
119impl SerializerConfigBuilder {
120    /// Creates a builder that starts with the default.
121    pub const fn new() -> SerializerConfigBuilder {
122        SerializerConfigBuilder {
123            value: SerializerConfig::new(),
124        }
125    }
126
127    /// Sets the context the values are serialized in.
128    ///
129    /// See [`SerializerConfig::set_context`].
130    pub fn context(mut self, context: deser_core::Context) -> SerializerConfigBuilder {
131        self.value.set_context(context);
132        self
133    }
134
135    /// Returns the built [`SerializerConfig`].
136    pub const fn build(self) -> SerializerConfig {
137        // the value cannot be moved out of the builder in a const fn as the
138        // builder needs dropping (the context has a destructor)
139        // SAFETY: the value is read once and the builder is forgotten
140        let value = unsafe { core::ptr::read(&self.value) };
141        core::mem::forget(self);
142        value
143    }
144}
145
146impl Default for SerializerConfigBuilder {
147    fn default() -> SerializerConfigBuilder {
148        SerializerConfigBuilder::new()
149    }
150}
151
152/// Serializes values into PHP's serialization format.
153///
154/// The values are appended to the output one after another (they can be
155/// read back with [`Deserializer::iter`](crate::Deserializer::iter)).
156///
157/// ```
158/// use deser_php::Serializer;
159///
160/// let mut serializer = Serializer::new();
161/// serializer.serialize(&true).unwrap();
162/// serializer.serialize(&"hi").unwrap();
163/// assert_eq!(serializer.output(), b"b:1;s:2:\"hi\";");
164/// ```
165///
166/// The serializer is also the stream serializer of the format (see
167/// [`StreamSerializer`](ser::StreamSerializer)).  Values are written once
168/// they are complete as the lengths of arrays are written in front of
169/// them.  To write to a [`Write`](std::io::Write) use
170/// [`SerializerConfig::writer`].
171#[derive(Debug, Clone, Default)]
172pub struct Serializer {
173    config: SerializerConfig,
174    out: Vec<u8>,
175}
176
177impl Serializer {
178    /// Creates a serializer.
179    pub fn new() -> Serializer {
180        Serializer::with_config(SerializerConfig::new())
181    }
182
183    /// Creates a serializer with the given configuration.
184    pub fn with_config(config: SerializerConfig) -> Serializer {
185        Serializer {
186            config,
187            out: Vec::new(),
188        }
189    }
190
191    /// Returns the configuration.
192    pub fn config(&self) -> &SerializerConfig {
193        &self.config
194    }
195
196    /// Serializes a value and appends it to the output.
197    pub fn serialize<T: Serialize + ?Sized>(&mut self, value: &T) -> Result<(), Error> {
198        ser::Serializer::serialize(self, value)
199    }
200
201    /// Serializes a value with a configured driver.
202    ///
203    /// The callback is invoked with the driver before the value is
204    /// serialized, for instance to add [`Layer`](deser_core::ser::Layer)s.
205    pub fn serialize_with<F, T: Serialize + ?Sized>(
206        &mut self,
207        value: &T,
208        setup: F,
209    ) -> Result<(), Error>
210    where
211        F: FnOnce(&mut SerializeDriver<'_>),
212    {
213        ser::Serializer::serialize_with(self, value, setup)
214    }
215
216    /// Returns the output written so far (that was not cleared).
217    pub fn output(&self) -> &[u8] {
218        &self.out
219    }
220
221    /// Returns the output.
222    pub fn finish(self) -> Vec<u8> {
223        self.out
224    }
225}
226
227impl ser::Serializer for Serializer {
228    fn drive(&mut self, driver: &mut SerializeDriver<'_>) -> Result<(), Error> {
229        self.config.apply_context(driver);
230        let bytes = serialize_driver(driver)?;
231        self.out.extend_from_slice(&bytes);
232        Ok(())
233    }
234}
235
236impl ser::StreamSerializer for Serializer {
237    fn output(&self) -> &[u8] {
238        &self.out
239    }
240
241    fn clear_output(&mut self) {
242        self.out.clear();
243    }
244}
245
246#[cfg(feature = "io")]
247impl SerializerConfig {
248    /// Creates a writer of values (see
249    /// [`deser::io::Writer`](deser_core::io::Writer)).
250    ///
251    /// The values are written one after another, each once it's complete.
252    pub fn writer<W: std::io::Write>(&self, writer: W) -> deser_core::io::Writer<W, Serializer> {
253        deser_core::io::Writer::new(writer, Serializer::with_config(self.clone()))
254    }
255
256    /// Serializes a value to a writer.
257    ///
258    /// See [`to_writer`].
259    pub fn to_writer<W: std::io::Write, T: Serialize + ?Sized>(
260        &self,
261        writer: W,
262        value: &T,
263    ) -> Result<(), Error> {
264        self.writer(writer).write(value)
265    }
266}
267
268/// Serializes a value to a writer.
269///
270/// ```
271/// let mut out = Vec::new();
272/// deser_php::to_writer(&mut out, &vec!["a", "b"]).unwrap();
273/// assert_eq!(out, br#"a:2:{i:0;s:1:"a";i:1;s:1:"b";}"#);
274/// ```
275#[cfg(feature = "io")]
276pub fn to_writer<W: std::io::Write, T: Serialize + ?Sized>(
277    writer: W,
278    value: &T,
279) -> Result<(), Error> {
280    SerializerConfig::new().to_writer(writer, value)
281}
282
283/// Serializes a value into PHP's serialization format.
284///
285/// This uses the default [`SerializerConfig`].
286///
287/// ```
288/// #[derive(deser::Serialize)]
289/// struct Package {
290///     name: String,
291///     tags: Vec<String>,
292/// }
293/// let package = Package { name: "deser".into(), tags: vec!["a".into()] };
294/// assert_eq!(
295///     deser_php::to_vec(&package).unwrap(),
296///     br#"a:2:{s:4:"name";s:5:"deser";s:4:"tags";a:1:{i:0;s:1:"a";}}"#
297/// );
298/// ```
299pub fn to_vec<T: Serialize + ?Sized>(value: &T) -> Result<Vec<u8>, Error> {
300    SerializerConfig::new().to_vec(value)
301}
302
303/// Serializes the value of a driver.
304fn serialize_driver(driver: &mut SerializeDriver<'_>) -> Result<Vec<u8>, Error> {
305    let mut writer = Writer::default();
306    driver.drive_sink(&mut writer)?;
307    writer.finish()
308}
309
310/// The kind of an open container.
311enum Container {
312    /// A sequence, written as array with the keys `0`, `1`, ...
313    List(u64),
314    Array,
315    Object(String),
316}
317
318/// An open container.
319struct Open {
320    container: Container,
321    /// The index of the header in `Writer::headers`.
322    header: usize,
323    /// The number of entries.
324    count: usize,
325    /// `true` if a key comes next (maps only).
326    expects_key: bool,
327}
328
329/// Writes the events of a value.
330///
331/// The number of entries is written in front of the entries of arrays and
332/// objects.  The headers are inserted once the value is complete.
333#[derive(Default)]
334struct Writer {
335    out: Vec<u8>,
336    stack: Vec<Open>,
337    /// The headers of the arrays and objects and where they go, in the
338    /// order of the output.
339    headers: Vec<(usize, Vec<u8>)>,
340    /// `true` once the top-level value started.
341    started: bool,
342}
343
344impl EventSink for Writer {
345    fn event(
346        &mut self,
347        event: Event<'_>,
348        _value: SerializeRef<'_>,
349        state: &mut State,
350    ) -> Result<(), Error> {
351        let Some(open) = self.stack.last_mut() else {
352            if self.started {
353                return Err(Error::new(ErrorKind::InvalidState, "unexpected event"));
354            }
355            self.started = true;
356            return self.value(event, state);
357        };
358        match open.container {
359            Container::List(ref mut index) => {
360                if event == Event::SeqEnd {
361                    return self.close();
362                }
363                let key = *index;
364                *index += 1;
365                open.count += 1;
366                self.out.extend_from_slice(b"i:");
367                push_int(&mut self.out, key as i128);
368                self.out.push(b';');
369                self.value(event, state)
370            }
371            _ if open.expects_key => {
372                if event == Event::MapEnd {
373                    return self.close();
374                }
375                open.expects_key = false;
376                open.count += 1;
377                let is_object = matches!(open.container, Container::Object(_));
378                let Event::Atom(atom) = event else {
379                    return Err(Error::new(
380                        ErrorKind::UnsupportedType,
381                        "keys must be integers or strings",
382                    ));
383                };
384                let visibility = state
385                    .event::<PropertyVisibility>()
386                    .and_then(|visibility| visibility.0.clone());
387                self.key(atom, visibility, is_object)
388            }
389            _ => {
390                open.expects_key = true;
391                self.value(event, state)
392            }
393        }
394    }
395}
396
397impl Writer {
398    /// Returns the output with the headers inserted.
399    fn finish(self) -> Result<Vec<u8>, Error> {
400        if !self.started || !self.stack.is_empty() {
401            return Err(Error::new(ErrorKind::InvalidState, "incomplete value"));
402        }
403        if self.headers.is_empty() {
404            return Ok(self.out);
405        }
406        let len = self
407            .headers
408            .iter()
409            .map(|(_, header)| header.len())
410            .sum::<usize>();
411        let mut out = Vec::with_capacity(self.out.len() + len);
412        let mut pos = 0;
413        for (offset, header) in &self.headers {
414            out.extend_from_slice(&self.out[pos..*offset]);
415            out.extend_from_slice(header);
416            pos = *offset;
417        }
418        out.extend_from_slice(&self.out[pos..]);
419        Ok(out)
420    }
421
422    /// Writes the first event of a value.
423    fn value(&mut self, event: Event<'_>, state: &mut State) -> Result<(), Error> {
424        let class = state
425            .event::<ClassName>()
426            .and_then(|class| class.0.as_ref());
427        match event {
428            Event::Atom(atom) => match class {
429                Some(class) => {
430                    let class = class.clone();
431                    self.classed_atom(atom, &class)
432                }
433                None => self.atom(atom),
434            },
435            Event::MapStart(_) => {
436                let container = match class {
437                    Some(class) => {
438                        check_class(class)?;
439                        Container::Object(class.clone())
440                    }
441                    None => Container::Array,
442                };
443                self.open(container);
444                Ok(())
445            }
446            Event::SeqStart(_) => {
447                if class.is_some() {
448                    return Err(Error::new(
449                        ErrorKind::UnsupportedType,
450                        "only maps, strings and bytes can have a class",
451                    ));
452                }
453                self.open(Container::List(0));
454                Ok(())
455            }
456            Event::MapEnd | Event::SeqEnd => {
457                Err(Error::new(ErrorKind::InvalidState, "unexpected end event"))
458            }
459        }
460    }
461
462    fn open(&mut self, container: Container) {
463        self.stack.push(Open {
464            container,
465            header: self.headers.len(),
466            count: 0,
467            expects_key: true,
468        });
469        self.headers.push((self.out.len(), Vec::new()));
470    }
471
472    /// Closes the innermost container and writes its header.
473    fn close(&mut self) -> Result<(), Error> {
474        let open = self.stack.pop().unwrap();
475        if !open.expects_key {
476            return Err(Error::new(ErrorKind::InvalidState, "map without value"));
477        }
478        let header = &mut self.headers[open.header].1;
479        match open.container {
480            Container::Object(class) => {
481                header.extend_from_slice(b"O:");
482                push_int(header, class.len() as i128);
483                header.extend_from_slice(b":\"");
484                header.extend_from_slice(class.as_bytes());
485                header.extend_from_slice(b"\":");
486            }
487            _ => header.extend_from_slice(b"a:"),
488        }
489        push_int(header, open.count as i128);
490        header.extend_from_slice(b":{");
491        self.out.push(b'}');
492        Ok(())
493    }
494
495    fn atom(&mut self, atom: Atom<'_>) -> Result<(), Error> {
496        match atom {
497            Atom::Null => self.out.extend_from_slice(b"N;"),
498            Atom::Bool(value) => self
499                .out
500                .extend_from_slice(if value { b"b:1;" } else { b"b:0;" }),
501            Atom::U64(value) => self.int(value.into())?,
502            Atom::I64(value) => self.int(value.into())?,
503            Atom::F32(value) => {
504                let mut text = String::new();
505                write_f32(value, &mut text);
506                self.float(&text);
507            }
508            Atom::F64(value) => {
509                let mut text = String::new();
510                write_f64(value, &mut text);
511                self.float(&text);
512            }
513            Atom::Str(value) | Atom::Lexical(value) => self.string(value.as_bytes()),
514            Atom::Char(value) => self.string(value.encode_utf8(&mut [0; 4]).as_bytes()),
515            Atom::Bytes(value) => self.string(value.data()),
516            // values whose type was inferred from text are written as value
517            Atom::Implicit(value) => return self.atom(value.value().to_atom()),
518            Atom::Ext(ref ext) => return self.ext(ext),
519            _ => return Err(Error::new(ErrorKind::UnsupportedType, "unknown atom")),
520        }
521        Ok(())
522    }
523
524    #[cold]
525    fn ext(&mut self, ext: &ExtValue<'_>) -> Result<(), Error> {
526        if let Some(reference) = ext.downcast_ref::<Reference>() {
527            self.out.extend_from_slice(match reference.kind() {
528                ReferenceKind::Object => b"r:",
529                ReferenceKind::Value => b"R:",
530            });
531            push_int(&mut self.out, reference.number().into());
532            self.out.push(b';');
533            return Ok(());
534        }
535        if let Some(value) = ext_int(ext) {
536            return self.int(value);
537        }
538        // numbers from text formats are integers if their text is one
539        if let Some(value) = ext.downcast_value_ref::<Number>() {
540            return match value.as_str().parse::<i64>() {
541                Ok(value) => self.int(value.into()),
542                Err(_) => self.atom(Atom::F64(value.value())),
543            };
544        }
545        match ext.fallback() {
546            Atom::Ext(_) => Err(Error::new(
547                ErrorKind::UnsupportedType,
548                format!("PHP's serialization format does not support {}", ext.name()),
549            )),
550            fallback => self.atom(fallback),
551        }
552    }
553
554    /// Writes an atom with a class: an enum case or a custom serialized
555    /// object.
556    fn classed_atom(&mut self, atom: Atom<'_>, class: &str) -> Result<(), Error> {
557        check_class(class)?;
558        match atom {
559            Atom::Str(ref case) | Atom::Lexical(ref case) => {
560                if !is_name(case.as_bytes()) {
561                    return Err(Error::new(ErrorKind::InvalidValue, "invalid enum case"));
562                }
563                self.out.extend_from_slice(b"E:");
564                push_int(&mut self.out, (class.len() + 1 + case.len()) as i128);
565                self.out.extend_from_slice(b":\"");
566                self.out.extend_from_slice(class.as_bytes());
567                self.out.push(b':');
568                self.out.extend_from_slice(case.as_bytes());
569                self.out.extend_from_slice(b"\";");
570            }
571            Atom::Bytes(ref payload) => {
572                self.out.extend_from_slice(b"C:");
573                push_int(&mut self.out, class.len() as i128);
574                self.out.extend_from_slice(b":\"");
575                self.out.extend_from_slice(class.as_bytes());
576                self.out.extend_from_slice(b"\":");
577                push_int(&mut self.out, payload.data().len() as i128);
578                self.out.extend_from_slice(b":{");
579                self.out.extend_from_slice(payload.data());
580                self.out.push(b'}');
581            }
582            _ => {
583                return Err(Error::new(
584                    ErrorKind::UnsupportedType,
585                    "only maps, strings and bytes can have a class",
586                ));
587            }
588        }
589        Ok(())
590    }
591
592    fn int(&mut self, value: i128) -> Result<(), Error> {
593        if i64::try_from(value).is_err() {
594            return Err(out_of_range());
595        }
596        self.out.extend_from_slice(b"i:");
597        push_int(&mut self.out, value);
598        self.out.push(b';');
599        Ok(())
600    }
601
602    fn float(&mut self, text: &str) {
603        self.out.extend_from_slice(b"d:");
604        self.out.extend_from_slice(text.as_bytes());
605        self.out.push(b';');
606    }
607
608    fn string(&mut self, bytes: &[u8]) {
609        self.out.extend_from_slice(b"s:");
610        push_int(&mut self.out, bytes.len() as i128);
611        self.out.extend_from_slice(b":\"");
612        self.out.extend_from_slice(bytes);
613        self.out.extend_from_slice(b"\";");
614    }
615
616    /// Writes a key.
617    ///
618    /// Arrays have integer and string keys, strings which are the text of
619    /// an integer are integers (like in PHP).  The keys of objects are the
620    /// names of properties, which are strings (with the prefix of their
621    /// visibility).
622    fn key(
623        &mut self,
624        atom: Atom<'_>,
625        visibility: Option<Visibility>,
626        is_object: bool,
627    ) -> Result<(), Error> {
628        let mut key = key_of(atom)?;
629        if let Some(visibility) = visibility.filter(|x| *x != Visibility::Public) {
630            let name = match key {
631                Key::Int(value) => Cow::Owned(value.to_string().into_bytes()),
632                Key::Str(name) => name,
633            };
634            let mut mangled = Vec::with_capacity(name.len() + 3);
635            mangled.push(0);
636            match visibility {
637                Visibility::Private(class) => mangled.extend_from_slice(class.as_bytes()),
638                _ => mangled.push(b'*'),
639            }
640            mangled.push(0);
641            mangled.extend_from_slice(&name);
642            key = Key::Str(Cow::Owned(mangled));
643        }
644        match key {
645            Key::Int(value) if !is_object => self.int(value.into()),
646            Key::Int(value) => {
647                self.string(value.to_string().as_bytes());
648                Ok(())
649            }
650            Key::Str(name) => {
651                match int_key(&name).filter(|_| !is_object) {
652                    Some(value) => self.int(value.into())?,
653                    None => self.string(&name),
654                }
655                Ok(())
656            }
657        }
658    }
659}
660
661/// A key of an array or object.
662enum Key<'a> {
663    Int(i64),
664    Str(Cow<'a, [u8]>),
665}
666
667/// Converts the atom of a key.
668fn key_of(atom: Atom<'_>) -> Result<Key<'_>, Error> {
669    Ok(match atom {
670        Atom::Str(value) | Atom::Lexical(value) => Key::Str(match value.into_cow() {
671            Cow::Borrowed(value) => Cow::Borrowed(value.as_bytes()),
672            Cow::Owned(value) => Cow::Owned(value.into_bytes()),
673        }),
674        Atom::Char(value) => Key::Str(Cow::Owned(value.to_string().into_bytes())),
675        Atom::Bytes(value) => Key::Str(value.into_data()),
676        Atom::U64(value) => Key::Int(i64::try_from(value).map_err(|_| out_of_range())?),
677        Atom::I64(value) => Key::Int(value),
678        // like PHP: `true` is the key `1`
679        Atom::Bool(value) => Key::Int(value.into()),
680        Atom::Implicit(value) => {
681            let (text, value) = value.into_parts();
682            match value {
683                ImplicitValue::U64(value) => {
684                    Key::Int(i64::try_from(value).map_err(|_| out_of_range())?)
685                }
686                ImplicitValue::I64(value) => Key::Int(value),
687                _ => Key::Str(Cow::Owned(text.into_owned().into_bytes())),
688            }
689        }
690        Atom::Ext(ref ext) => {
691            if let Some(value) = ext_int(ext) {
692                Key::Int(i64::try_from(value).map_err(|_| out_of_range())?)
693            } else if ext.is::<Reference>() {
694                return Err(unsupported_key());
695            } else {
696                match ext.fallback() {
697                    Atom::Ext(_) => return Err(unsupported_key()),
698                    fallback => match key_of(fallback)? {
699                        Key::Int(value) => Key::Int(value),
700                        Key::Str(name) => Key::Str(Cow::Owned(name.into_owned())),
701                    },
702                }
703            }
704        }
705        _ => return Err(unsupported_key()),
706    })
707}
708
709/// Returns the value of the extension types for integers.
710fn ext_int(ext: &ExtValue<'_>) -> Option<i128> {
711    if let Some(&value) = ext.downcast_ref::<u128>() {
712        // out of range values fail
713        return Some(i128::try_from(value).unwrap_or(i128::MAX));
714    }
715    if let Some(&value) = ext.downcast_ref::<i128>() {
716        return Some(value);
717    }
718    ext.downcast_ref::<BigInt>().map(|value| {
719        value.to_i128().unwrap_or(if value.is_negative() {
720            i128::MIN
721        } else {
722            i128::MAX
723        })
724    })
725}
726
727fn check_class(class: &str) -> Result<(), Error> {
728    if !is_class_name(class.as_bytes()) {
729        return Err(Error::new(
730            ErrorKind::InvalidValue,
731            format!("invalid class name {:?}", class),
732        ));
733    }
734    Ok(())
735}
736
737/// Appends the decimal text of an integer.
738fn push_int(out: &mut Vec<u8>, value: i128) {
739    let mut buf = [0u8; 40];
740    let mut pos = buf.len();
741    let negative = value < 0;
742    let mut rest = value.unsigned_abs();
743    loop {
744        pos -= 1;
745        buf[pos] = b'0' + (rest % 10) as u8;
746        rest /= 10;
747        if rest == 0 {
748            break;
749        }
750    }
751    if negative {
752        pos -= 1;
753        buf[pos] = b'-';
754    }
755    out.extend_from_slice(&buf[pos..]);
756}
757
758#[cold]
759fn out_of_range() -> Error {
760    Error::new(
761        ErrorKind::OutOfRange,
762        "integer out of range for PHP's serialization format",
763    )
764}
765
766#[cold]
767fn unsupported_key() -> Error {
768    Error::new(
769        ErrorKind::UnsupportedType,
770        "keys must be integers or strings",
771    )
772}