1
use std::fmt::Display;
2

            
3
use byteorder::{BigEndian, LittleEndian, ReadBytesExt, WriteBytesExt};
4
use half::f16;
5

            
6
pub(crate) const CURRENT_VERSION: u8 = 0;
7

            
8
use crate::reader::{BufferedBytes, Reader};
9
use crate::Error;
10
/// Writes an atom header into `writer`.
11
#[allow(clippy::cast_possible_truncation)]
12
#[inline]
13
1659956
fn write_tiny_atom_header<W: WriteBytesExt>(
14
1659956
    mut writer: W,
15
1659956
    kind: Kind,
16
1659956
    arg: u8,
17
1659956
) -> std::io::Result<usize> {
18
1659956
    // Kind is the 3 bits.
19
1659956
    let mut first_byte = (kind as u8) << 5;
20
1659956
    if arg > 0 {
21
1589205
        debug_assert!(arg < 0x10);
22
1589205
        first_byte |= arg & 0b1111;
23
70751
    }
24

            
25
1659956
    writer.write_all(&[first_byte])?;
26
1659956
    Ok(1)
27
1659956
}
28

            
29
/// Writes an atom header into `writer`.
30
#[allow(clippy::cast_possible_truncation)]
31
#[inline]
32
1963505
pub fn write_atom_header<W: WriteBytesExt>(
33
1963505
    mut writer: W,
34
1963505
    kind: Kind,
35
1963505
    mut arg: u64,
36
1963505
) -> std::io::Result<usize> {
37
1963505
    if arg < 0x10 {
38
1379687
        write_tiny_atom_header(writer, kind, arg as u8)
39
    } else {
40
        // Kind is the 3 bits.
41
583818
        let mut first_byte = (kind as u8) << 5;
42
583818
        // The last 4 bits are the first 4 bits of the arg. We also know
43
583818
        // that we're longer than one byte, due to the original match.
44
583818
        first_byte |= arg as u8 & 0b1111;
45
583818
        arg >>= 4;
46
583818
        first_byte |= 0b10000;
47
583818

            
48
583818
        let mut second = arg as u8 & 0x7F;
49
583818
        arg >>= 7;
50
583818
        if arg == 0 {
51
583760
            writer.write_all(&[first_byte, second])?;
52
583760
            return Ok(2);
53
58
        }
54
58

            
55
58
        second |= 0b1000_0000;
56
58
        let mut third = arg as u8 & 0x7F;
57
58
        arg >>= 7;
58
58
        if arg == 0 {
59
11
            writer.write_all(&[first_byte, second, third])?;
60
11
            return Ok(3);
61
47
        }
62
47

            
63
47
        third |= 0b1000_0000;
64
47
        let mut fourth = arg as u8 & 0x7F;
65
47
        arg >>= 7;
66
47
        if arg == 0 {
67
7
            writer.write_all(&[first_byte, second, third, fourth])?;
68
7
            return Ok(4);
69
40
        }
70
40

            
71
40
        fourth |= 0b1000_0000;
72
40
        let mut fifth = arg as u8 & 0x7F;
73
40
        arg >>= 7;
74
40
        if arg == 0 {
75
7
            writer.write_all(&[first_byte, second, third, fourth, fifth])?;
76
7
            return Ok(5);
77
33
        }
78
33

            
79
33
        fifth |= 0b1000_0000;
80
33
        let mut sixth = arg as u8 & 0x7F;
81
33
        arg >>= 7;
82
33
        if arg == 0 {
83
7
            writer.write_all(&[first_byte, second, third, fourth, fifth, sixth])?;
84
7
            return Ok(6);
85
26
        }
86
26
        sixth |= 0b1000_0000;
87
26
        let mut seventh = arg as u8 & 0x7F;
88
26
        arg >>= 7;
89
26
        if arg == 0 {
90
7
            writer.write_all(&[first_byte, second, third, fourth, fifth, sixth, seventh])?;
91
7
            return Ok(7);
92
19
        }
93
19
        seventh |= 0b1000_0000;
94
19
        let mut eighth = arg as u8 & 0x7F;
95
19
        arg >>= 7;
96
19
        if arg == 0 {
97
7
            writer.write_all(&[
98
7
                first_byte, second, third, fourth, fifth, sixth, seventh, eighth,
99
7
            ])?;
100
7
            return Ok(8);
101
12
        }
102
12

            
103
12
        eighth |= 0b1000_0000;
104
12
        let mut ninth = arg as u8 & 0x7F;
105
12
        arg >>= 7;
106
12
        if arg == 0 {
107
7
            writer.write_all(&[
108
7
                first_byte, second, third, fourth, fifth, sixth, seventh, eighth, ninth,
109
7
            ])?;
110
7
            return Ok(9);
111
5
        }
112
5

            
113
5
        ninth |= 0b1000_0000;
114
5
        debug_assert!(arg <= 255);
115
5
        writer.write_all(&[
116
5
            first_byte, second, third, fourth, fifth, sixth, seventh, eighth, ninth, arg as u8,
117
5
        ])?;
118
5
        Ok(10)
119
    }
120
1963505
}
121

            
122
/// Reads an atom header (kind and argument).
123
#[inline]
124
160842
pub fn read_atom_header<R: ReadBytesExt>(reader: &mut R) -> Result<(Kind, u64), Error> {
125
160842
    let first_byte = reader.read_u8()?;
126
160842
    let kind = Kind::from_u8(first_byte >> 5)?;
127
160842
    let mut arg = u64::from(first_byte & 0b1111);
128
160842
    if first_byte & 0b10000 != 0 {
129
42043
        let mut bytes_remaining = 9;
130
42043
        let mut offset = 4;
131
        loop {
132
42280
            let byte = reader.read_u8()?;
133
42280
            let data = byte & 0x7f;
134
42280
            arg |= u64::from(data) << offset;
135
42280
            offset += 7;
136
42280
            bytes_remaining -= 1;
137
42280
            if data == byte || bytes_remaining == 0 {
138
42043
                break;
139
237
            }
140
        }
141
118799
    }
142

            
143
160842
    Ok((kind, arg))
144
160842
}
145

            
146
/// The type of an atom.
147
10234
#[derive(Clone, Copy, Eq, PartialEq, Debug)]
148
pub enum Kind {
149
    /// A value with a special meaning.
150
    Special = 0,
151
    /// A signed integer. Argument is the byte length, minus one. The following
152
    /// bytes are the value, stored in little endian.
153
    Int = 1,
154
    /// An unsigned integer. Argument is the byte length, minus one. The
155
    /// following bytes are the value, stored in little endian.
156
    UInt = 2,
157
    /// A floating point value. Argument is the byte length, minus one. Must be
158
    /// either 2, 4 or 8 bytes. The following bytes are the value, stored in
159
    /// little endian. The two-byte representation follows the IEEE 754-2008
160
    /// standard, implemented by the [`half`] crate.
161
    Float = 3,
162
    /// A list of atoms. Argument is the count of atoms in the sequence.
163
    Sequence = 4,
164
    /// A list of key-value pairs. Argument is the count of entries in the map.
165
    /// There will be twice as many total atoms, since each entry is a key/value
166
    /// pair.
167
    Map = 5,
168
    /// A symbol. If the least-significant bit of the arg is 0, this is a new
169
    /// symbol. The remaining bits of the arg contain the length in bytes. The
170
    /// following bytes will contain the symbol bytes (UTF-8). It should be
171
    /// stored and given a unique symbol id, starting at 0.
172
    ///
173
    /// If the least-significant bit of the arg is 1, the remaining bits are the
174
    /// symbol id of a previously emitted symbol.
175
    Symbol = 6,
176
    /// A series of bytes. The argument is the length. The bytes follow.
177
    Bytes = 7,
178
}
179

            
180
impl Kind {
181
    /// Converts from a u8. Returns an error if `kind` is an invalid value.
182
    #[inline]
183
160858
    pub const fn from_u8(kind: u8) -> Result<Self, Error> {
184
160858
        match kind {
185
15126
            0 => Ok(Self::Special),
186
138
            1 => Ok(Self::Int),
187
20177
            2 => Ok(Self::UInt),
188
46
            3 => Ok(Self::Float),
189
19
            4 => Ok(Self::Sequence),
190
10098
            5 => Ok(Self::Map),
191
80255
            6 => Ok(Self::Symbol),
192
34991
            7 => Ok(Self::Bytes),
193
8
            other => Err(Error::InvalidKind(other)),
194
        }
195
160858
    }
196
}
197

            
198
/// A special value type.
199
#[derive(Debug)]
200
pub enum Special {
201
    /// A None value.
202
    None = 0,
203
    /// A Unit value.
204
    Unit = 1,
205
    /// The `false` boolean literal.
206
    False = 2,
207
    /// The `true` boolean literal.
208
    True = 3,
209
    /// A named value. A symbol followed by another value.
210
    Named = 4,
211
    /// A sequence of key-value pairs with an unknown length.
212
    DynamicMap = 5,
213
    /// A terminal value for a [`Self::DynamicMap`].
214
    DynamicEnd = 6,
215
}
216

            
217
#[cfg(test)]
218
pub(crate) const SPECIAL_COUNT: u64 = Special::Named as u64 + 1;
219

            
220
impl TryFrom<u64> for Special {
221
    type Error = UnknownSpecial;
222

            
223
    #[inline]
224
15126
    fn try_from(value: u64) -> Result<Self, Self::Error> {
225
15126
        match value {
226
5081
            0 => Ok(Self::None),
227
20
            1 => Ok(Self::Unit),
228
1
            2 => Ok(Self::False),
229
3
            3 => Ok(Self::True),
230
10015
            4 => Ok(Self::Named),
231
3
            5 => Ok(Self::DynamicMap),
232
2
            6 => Ok(Self::DynamicEnd),
233
1
            _ => Err(UnknownSpecial(value)),
234
        }
235
15126
    }
236
}
237

            
238
1
#[test]
239
1
fn unknown_special() {
240
1
    let err = Special::try_from(u64::MAX).unwrap_err();
241
1
    assert_eq!(err, UnknownSpecial(u64::MAX));
242
1
    assert!(err.to_string().contains("unknown special"));
243
1
}
244

            
245
/// An unknown [`Special`] was encountered.
246
1
#[derive(Debug, Clone, Copy, Eq, PartialEq)]
247
pub struct UnknownSpecial(pub u64);
248

            
249
impl Display for UnknownSpecial {
250
1
    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
251
1
        write!(f, "unknown special id: {}", self.0)
252
1
    }
253
}
254

            
255
/// Writes the Pot header. A u32 written in big endian. The first three bytes
256
/// are 'Pot' (`0x506F74`), and the fourth byte is the version. The first
257
/// version of Pot is 0.
258
#[inline]
259
pub fn write_header<W: WriteBytesExt>(mut writer: W, version: u8) -> std::io::Result<usize> {
260
1176
    writer.write_u32::<BigEndian>(0x506F_7400 | u32::from(version))?;
261
1176
    Ok(4)
262
1176
}
263

            
264
/// Reads a Pot header. See [`write_header`] for more information. Returns the version number contained within.
265
#[allow(clippy::similar_names, clippy::cast_possible_truncation)]
266
#[inline]
267
230
pub fn read_header<R: ReadBytesExt>(reader: &mut R) -> Result<u8, Error> {
268
230
    let header = reader.read_u32::<BigEndian>()?;
269
230
    if header & 0x506F_7400 == 0x506F_7400 {
270
229
        let version = (header & 0xFF) as u8;
271
229
        Ok(version)
272
    } else {
273
1
        Err(Error::IncompatibleVersion)
274
    }
275
230
}
276
/// Writes a [`Kind::Special`] atom.
277
#[inline]
278
210050
pub fn write_special<W: WriteBytesExt>(writer: W, special: Special) -> std::io::Result<usize> {
279
210050
    write_atom_header(writer, Kind::Special, special as u64)
280
210050
}
281

            
282
/// Writes a [`Kind::Special`] atom with [`Special::None`].
283
#[inline]
284
70020
pub fn write_none<W: WriteBytesExt>(writer: W) -> std::io::Result<usize> {
285
70020
    write_special(writer, Special::None)
286
70020
}
287

            
288
/// Writes a [`Kind::Special`] atom with [`Special::Unit`].
289
#[inline]
290
7
pub fn write_unit<W: WriteBytesExt>(writer: W) -> std::io::Result<usize> {
291
7
    write_special(writer, Special::Unit)
292
7
}
293

            
294
/// Writes a [`Kind::Special`] atom with [`Special::Named`].
295
#[inline]
296
140015
pub fn write_named<W: WriteBytesExt>(writer: W) -> std::io::Result<usize> {
297
140015
    write_special(writer, Special::Named)
298
140015
}
299

            
300
/// Writes a [`Kind::Special`] atom with either [`Special::True`] or [`Special::False`].
301
#[inline]
302
4
pub fn write_bool<W: WriteBytesExt>(writer: W, boolean: bool) -> std::io::Result<usize> {
303
4
    write_special(
304
4
        writer,
305
4
        if boolean {
306
3
            Special::True
307
        } else {
308
1
            Special::False
309
        },
310
    )
311
4
}
312

            
313
/// Writes an [`Kind::Int`] atom with the given value. Will encode in a smaller format if possible.
314
#[inline]
315
67
pub fn write_i8<W: WriteBytesExt>(mut writer: W, value: i8) -> std::io::Result<usize> {
316
67
    let header_len =
317
67
        write_atom_header(&mut writer, Kind::Int, std::mem::size_of::<i8>() as u64 - 1)?;
318
67
    writer
319
67
        .write_i8(value)
320
67
        .map(|_| std::mem::size_of::<i8>() + header_len)
321
67
}
322

            
323
/// Writes an [`Kind::Int`] atom with the given value. Will encode in a smaller format if possible.
324
#[inline]
325
pub fn write_i16<W: WriteBytesExt>(mut writer: W, value: i16) -> std::io::Result<usize> {
326
64
    if let Ok(value) = i8::try_from(value) {
327
50
        write_i8(writer, value)
328
    } else {
329
14
        let header_len = write_tiny_atom_header(&mut writer, Kind::Int, 2 - 1)?;
330
14
        writer
331
14
            .write_i16::<LittleEndian>(value)
332
14
            .map(|_| 2 + header_len)
333
    }
334
64
}
335

            
336
/// Writes an [`Kind::Int`] atom with the given value. Will encode in a smaller format if possible.
337
#[inline]
338
pub fn write_i24<W: WriteBytesExt>(mut writer: W, value: i32) -> std::io::Result<usize> {
339
56
    if let Ok(value) = i16::try_from(value) {
340
48
        write_i16(writer, value)
341
    } else {
342
8
        let header_len = write_tiny_atom_header(&mut writer, Kind::Int, 3 - 1)?;
343
8
        writer
344
8
            .write_i24::<LittleEndian>(value)
345
8
            .map(|_| 3 + header_len)
346
    }
347
56
}
348

            
349
/// Writes an [`Kind::Int`] atom with the given value. Will encode in a smaller format if possible.
350
#[inline]
351
70
pub fn write_i32<W: WriteBytesExt>(mut writer: W, value: i32) -> std::io::Result<usize> {
352
70
    if value >= -(2_i32.pow(23)) && value < 2_i32.pow(23) {
353
56
        write_i24(writer, value)
354
    } else {
355
14
        let header_len = write_tiny_atom_header(&mut writer, Kind::Int, 4 - 1)?;
356
14
        writer
357
14
            .write_i32::<LittleEndian>(value)
358
14
            .map(|_| 4 + header_len)
359
    }
360
70
}
361

            
362
/// Writes an [`Kind::Int`] atom with the given value. Will encode in a smaller format if possible.
363
#[inline]
364
pub fn write_i48<W: WriteBytesExt>(mut writer: W, value: i64) -> std::io::Result<usize> {
365
58
    if let Ok(value) = i32::try_from(value) {
366
50
        write_i32(writer, value)
367
    } else {
368
8
        let header_len = write_tiny_atom_header(&mut writer, Kind::Int, 6 - 1)?;
369
8
        writer
370
8
            .write_i48::<LittleEndian>(value)
371
8
            .map(|_| 6 + header_len)
372
    }
373
58
}
374

            
375
/// Writes an [`Kind::Int`] atom with the given value. Will encode in a smaller format if possible.
376
#[inline]
377
70
pub fn write_i64<W: WriteBytesExt>(mut writer: W, value: i64) -> std::io::Result<usize> {
378
70
    if value >= -(2_i64.pow(47)) && value < 2_i64.pow(47) {
379
58
        write_i48(writer, value)
380
    } else {
381
12
        let header_len = write_tiny_atom_header(&mut writer, Kind::Int, 8 - 1)?;
382
12
        writer
383
12
            .write_i64::<LittleEndian>(value)
384
12
            .map(|_| 8 + header_len)
385
    }
386
70
}
387

            
388
/// Writes an [`Kind::Int`] atom with the given value. Will encode in a smaller format if possible.
389
#[inline]
390
pub fn write_i128<W: WriteBytesExt>(mut writer: W, value: i128) -> std::io::Result<usize> {
391
62
    if let Ok(value) = i64::try_from(value) {
392
54
        write_i64(writer, value)
393
    } else {
394
8
        let header_len = write_tiny_atom_header(&mut writer, Kind::Int, 16 - 1)?;
395
8
        writer
396
8
            .write_i128::<LittleEndian>(value)
397
8
            .map(|_| 16 + header_len)
398
    }
399
62
}
400

            
401
/// Writes an [`Kind::UInt`] atom with the given value.
402
#[inline]
403
663
pub fn write_u8<W: WriteBytesExt>(mut writer: W, value: u8) -> std::io::Result<usize> {
404
663
    let header_len = write_tiny_atom_header(&mut writer, Kind::UInt, 0)?;
405
663
    writer
406
663
        .write_u8(value)
407
663
        .map(|_| std::mem::size_of::<u8>() + header_len)
408
663
}
409

            
410
/// Writes an [`Kind::Int`] atom with the given value. Will encode in a smaller format if possible.
411
#[inline]
412
pub fn write_u16<W: WriteBytesExt>(mut writer: W, value: u16) -> std::io::Result<usize> {
413
140093
    if let Ok(value) = u8::try_from(value) {
414
635
        write_u8(writer, value)
415
    } else {
416
139458
        let header_len = write_tiny_atom_header(&mut writer, Kind::UInt, 1)?;
417
139458
        writer
418
139458
            .write_u16::<LittleEndian>(value)
419
139458
            .map(|_| std::mem::size_of::<u16>() + header_len)
420
    }
421
140093
}
422

            
423
/// Writes an [`Kind::Int`] atom with the given value. Will encode in a smaller format if possible.
424
#[inline]
425
pub fn write_u24<W: WriteBytesExt>(mut writer: W, value: u32) -> std::io::Result<usize> {
426
83
    if let Ok(value) = u16::try_from(value) {
427
75
        write_u16(writer, value)
428
    } else {
429
8
        let header_len = write_tiny_atom_header(&mut writer, Kind::UInt, 2)?;
430
8
        writer
431
8
            .write_u24::<LittleEndian>(value)
432
8
            .map(|_| 3 + header_len)
433
    }
434
83
}
435

            
436
/// Writes an [`Kind::Int`] atom with the given value. Will encode in a smaller format if possible.
437
#[inline]
438
92
pub fn write_u32<W: WriteBytesExt>(mut writer: W, value: u32) -> std::io::Result<usize> {
439
92
    if value < 2_u32.pow(24) {
440
83
        write_u24(writer, value)
441
    } else {
442
9
        let header_len = write_tiny_atom_header(&mut writer, Kind::UInt, 3)?;
443
9
        writer
444
9
            .write_u32::<LittleEndian>(value)
445
9
            .map(|_| std::mem::size_of::<u32>() + header_len)
446
    }
447
92
}
448

            
449
/// Writes an [`Kind::Int`] atom with the given value. Will encode in a smaller format if possible.
450
#[inline]
451
pub fn write_u48<W: WriteBytesExt>(mut writer: W, value: u64) -> std::io::Result<usize> {
452
65
    if let Ok(value) = u32::try_from(value) {
453
61
        write_u32(writer, value)
454
    } else {
455
4
        let header_len = write_tiny_atom_header(&mut writer, Kind::UInt, 5)?;
456
4
        writer
457
4
            .write_u48::<LittleEndian>(value)
458
4
            .map(|_| 6 + header_len)
459
    }
460
65
}
461

            
462
/// Writes an [`Kind::Int`] atom with the given value. Will encode in a smaller format if possible.
463
#[inline]
464
140079
pub fn write_u64<W: WriteBytesExt>(mut writer: W, value: u64) -> std::io::Result<usize> {
465
140079
    if value < 2_u64.pow(48) {
466
65
        write_u48(writer, value)
467
    } else {
468
140014
        let header_len = write_tiny_atom_header(&mut writer, Kind::UInt, 7)?;
469
140014
        writer
470
140014
            .write_u64::<LittleEndian>(value)
471
140014
            .map(|_| std::mem::size_of::<u64>() + header_len)
472
    }
473
140079
}
474

            
475
/// Writes an [`Kind::Int`] atom with the given value. Will encode in a smaller format if possible.
476
#[inline]
477
pub fn write_u128<W: WriteBytesExt>(mut writer: W, value: u128) -> std::io::Result<usize> {
478
41
    if let Ok(value) = u64::try_from(value) {
479
34
        write_u64(writer, value)
480
    } else {
481
7
        let header_len = write_tiny_atom_header(&mut writer, Kind::UInt, 15)?;
482
7
        writer
483
7
            .write_u128::<LittleEndian>(value)
484
7
            .map(|_| std::mem::size_of::<u128>() + header_len)
485
    }
486
41
}
487

            
488
/// Writes an [`Kind::Float`] atom with the given value.
489
#[inline]
490
#[allow(clippy::cast_possible_truncation, clippy::float_cmp)]
491
38
pub fn write_f32<W: WriteBytesExt>(mut writer: W, value: f32) -> std::io::Result<usize> {
492
38
    let as_f16 = f16::from_f32(value);
493
38
    if as_f16.to_f32() == value {
494
34
        let header_len = write_tiny_atom_header(
495
34
            &mut writer,
496
34
            Kind::Float,
497
34
            std::mem::size_of::<u16>() as u8 - 1,
498
34
        )?;
499
34
        writer
500
34
            .write_u16::<LittleEndian>(as_f16.to_bits())
501
34
            .map(|_| std::mem::size_of::<u16>() + header_len)
502
    } else {
503
4
        let header_len = write_tiny_atom_header(
504
4
            &mut writer,
505
4
            Kind::Float,
506
4
            std::mem::size_of::<f32>() as u8 - 1,
507
4
        )?;
508
4
        writer
509
4
            .write_f32::<LittleEndian>(value)
510
4
            .map(|_| std::mem::size_of::<f32>() + header_len)
511
    }
512
38
}
513

            
514
34
fn read_f16<R: ReadBytesExt>(reader: &mut R) -> std::io::Result<f32> {
515
34
    let value = f16::from_bits(reader.read_u16::<LittleEndian>()?);
516
34
    Ok(value.to_f32())
517
34
}
518

            
519
/// Writes an [`Kind::Float`] atom with the given value.
520
#[allow(clippy::cast_possible_truncation, clippy::float_cmp)]
521
#[inline]
522
25
pub fn write_f64<W: WriteBytesExt>(mut writer: W, value: f64) -> std::io::Result<usize> {
523
25
    let as_f32 = value as f32;
524
25
    if f64::from(as_f32) == value {
525
21
        write_f32(writer, as_f32)
526
    } else {
527
4
        let header_len = write_tiny_atom_header(&mut writer, Kind::Float, 7)?;
528
4
        writer
529
4
            .write_f64::<LittleEndian>(value)
530
4
            .map(|_| std::mem::size_of::<f64>() + header_len)
531
    }
532
25
}
533

            
534
/// Writes an [`Kind::Bytes`] atom with the bytes of the string.
535
#[inline]
536
490017
pub fn write_str<W: WriteBytesExt>(writer: W, value: &str) -> std::io::Result<usize> {
537
490017
    write_bytes(writer, value.as_bytes())
538
490017
}
539

            
540
/// Writes an [`Kind::Bytes`] atom with the given value.
541
#[inline]
542
490029
pub fn write_bytes<W: WriteBytesExt>(mut writer: W, value: &[u8]) -> std::io::Result<usize> {
543
490029
    let header_len = write_atom_header(&mut writer, Kind::Bytes, value.len() as u64)?;
544
490029
    writer.write_all(value)?;
545
490029
    Ok(value.len() + header_len)
546
490029
}
547

            
548
/// An integer type that can safely convert between other number types using compile-time evaluation.
549
45
#[derive(Clone, Copy, Debug, Eq, PartialEq)]
550
pub struct Integer(pub(crate) InnerInteger);
551

            
552
45
#[derive(Clone, Copy, Debug, Eq, PartialEq)]
553
pub(crate) enum InnerInteger {
554
    /// An i8 value.
555
    I8(i8),
556
    /// An i16 value.
557
    I16(i16),
558
    /// An i32 value.
559
    I32(i32),
560
    /// An i64 value.
561
    I64(i64),
562
    /// An i128 value.
563
    I128(i128),
564
    /// An u8 value.
565
    U8(u8),
566
    /// An u16 value.
567
    U16(u16),
568
    /// An u32 value.
569
    U32(u32),
570
    /// An u64 value.
571
    U64(u64),
572
    /// An u128 value.
573
    U128(u128),
574
}
575

            
576
impl Display for Integer {
577
27
    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
578
27
        match &self.0 {
579
6
            InnerInteger::I8(value) => Display::fmt(value, f),
580
1
            InnerInteger::I16(value) => Display::fmt(value, f),
581
1
            InnerInteger::I32(value) => Display::fmt(value, f),
582
1
            InnerInteger::I64(value) => Display::fmt(value, f),
583
1
            InnerInteger::I128(value) => Display::fmt(value, f),
584
13
            InnerInteger::U8(value) => Display::fmt(value, f),
585
1
            InnerInteger::U16(value) => Display::fmt(value, f),
586
1
            InnerInteger::U32(value) => Display::fmt(value, f),
587
1
            InnerInteger::U64(value) => Display::fmt(value, f),
588
1
            InnerInteger::U128(value) => Display::fmt(value, f),
589
        }
590
27
    }
591
}
592

            
593
impl Integer {
594
    /// Returns true if the value contained is zero.
595
    #[must_use]
596
    #[inline]
597
14
    pub const fn is_zero(&self) -> bool {
598
14
        match &self.0 {
599
4
            InnerInteger::I8(value) => *value == 0,
600
1
            InnerInteger::I16(value) => *value == 0,
601
1
            InnerInteger::I32(value) => *value == 0,
602
1
            InnerInteger::I64(value) => *value == 0,
603
1
            InnerInteger::I128(value) => *value == 0,
604
2
            InnerInteger::U8(value) => *value == 0,
605
1
            InnerInteger::U16(value) => *value == 0,
606
1
            InnerInteger::U32(value) => *value == 0,
607
1
            InnerInteger::U64(value) => *value == 0,
608
1
            InnerInteger::U128(value) => *value == 0,
609
        }
610
14
    }
611

            
612
    /// Returns the contained value as an i8, or an error if the value is unable to fit.
613
    // clippy::checked_conversions: try_from isn't const, and it would demote this from a const fn.
614
    #[allow(clippy::cast_possible_wrap)]
615
    #[allow(clippy::checked_conversions)]
616
    #[inline]
617
41
    pub const fn as_i8(&self) -> Result<i8, Error> {
618
41
        match &self.0 {
619
21
            InnerInteger::I8(value) => Ok(*value),
620
4
            InnerInteger::U8(value) => {
621
4
                if *value <= i8::MAX as u8 {
622
1
                    Ok(*value as i8)
623
                } else {
624
3
                    Err(Error::ImpreciseCastWouldLoseData)
625
                }
626
            }
627
16
            _ => Err(Error::ImpreciseCastWouldLoseData),
628
        }
629
41
    }
630

            
631
    /// Returns the contained value as an u8, or an error if the value is unable to fit.
632
    #[allow(clippy::cast_sign_loss)]
633
    #[inline]
634
61
    pub const fn as_u8(&self) -> Result<u8, Error> {
635
61
        match &self.0 {
636
45
            InnerInteger::U8(value) => Ok(*value),
637
4
            InnerInteger::I8(value) => {
638
4
                if *value >= 0 {
639
3
                    Ok(*value as u8)
640
                } else {
641
1
                    Err(Error::ImpreciseCastWouldLoseData)
642
                }
643
            }
644
12
            _ => Err(Error::ImpreciseCastWouldLoseData),
645
        }
646
61
    }
647

            
648
    /// Returns the contained value as an i16, or an error if the value is unable to fit.
649
    #[allow(clippy::cast_possible_wrap)]
650
    #[allow(clippy::checked_conversions)]
651
    #[inline]
652
41
    pub const fn as_i16(&self) -> Result<i16, Error> {
653
41
        match &self.0 {
654
12
            InnerInteger::I8(value) => Ok(*value as i16),
655
2
            InnerInteger::U8(value) => Ok(*value as i16),
656
11
            InnerInteger::I16(value) => Ok(*value),
657
4
            InnerInteger::U16(value) => {
658
4
                if *value <= i16::MAX as u16 {
659
1
                    Ok(*value as i16)
660
                } else {
661
3
                    Err(Error::ImpreciseCastWouldLoseData)
662
                }
663
            }
664
            InnerInteger::U32(_)
665
            | InnerInteger::I32(_)
666
            | InnerInteger::U64(_)
667
            | InnerInteger::I64(_)
668
            | InnerInteger::U128(_)
669
12
            | InnerInteger::I128(_) => Err(Error::ImpreciseCastWouldLoseData),
670
        }
671
41
    }
672

            
673
    /// Returns the contained value as an u16, or an error if the value is unable to fit.
674
    #[allow(clippy::cast_sign_loss)]
675
    #[inline]
676
10037
    pub const fn as_u16(&self) -> Result<u16, Error> {
677
10037
        match &self.0 {
678
2
            InnerInteger::I8(value) => {
679
2
                if *value >= 0 {
680
1
                    Ok(*value as u16)
681
                } else {
682
1
                    Err(Error::ImpreciseCastWouldLoseData)
683
                }
684
            }
685
49
            InnerInteger::U8(value) => Ok(*value as u16),
686
9973
            InnerInteger::U16(value) => Ok(*value),
687
4
            InnerInteger::I16(value) => {
688
4
                if *value >= 0 {
689
3
                    Ok(*value as u16)
690
                } else {
691
1
                    Err(Error::ImpreciseCastWouldLoseData)
692
                }
693
            }
694
            InnerInteger::U32(_)
695
            | InnerInteger::I32(_)
696
            | InnerInteger::U64(_)
697
            | InnerInteger::I64(_)
698
            | InnerInteger::U128(_)
699
9
            | InnerInteger::I128(_) => Err(Error::ImpreciseCastWouldLoseData),
700
        }
701
10037
    }
702

            
703
    /// Returns the contained value as an i32, or an error if the value is unable to fit.
704
    #[allow(clippy::cast_possible_wrap)]
705
    #[allow(clippy::checked_conversions)]
706
    #[inline]
707
55
    pub const fn as_i32(&self) -> Result<i32, Error> {
708
55
        match &self.0 {
709
19
            InnerInteger::I8(value) => Ok(*value as i32),
710
3
            InnerInteger::U8(value) => Ok(*value as i32),
711
2
            InnerInteger::I16(value) => Ok(*value as i32),
712
2
            InnerInteger::U16(value) => Ok(*value as i32),
713
15
            InnerInteger::I32(value) => Ok(*value),
714
6
            InnerInteger::U32(value) => {
715
6
                if *value <= i32::MAX as u32 {
716
1
                    Ok(*value as i32)
717
                } else {
718
5
                    Err(Error::ImpreciseCastWouldLoseData)
719
                }
720
            }
721
            InnerInteger::U64(_)
722
            | InnerInteger::I64(_)
723
            | InnerInteger::U128(_)
724
8
            | InnerInteger::I128(_) => Err(Error::ImpreciseCastWouldLoseData),
725
        }
726
55
    }
727

            
728
    /// Returns the contained value as an u32, or an error if the value is unable to fit.
729
    #[allow(clippy::cast_sign_loss)]
730
    #[inline]
731
53
    pub const fn as_u32(&self) -> Result<u32, Error> {
732
53
        match &self.0 {
733
2
            InnerInteger::I8(value) => {
734
2
                if *value >= 0 {
735
1
                    Ok(*value as u32)
736
                } else {
737
1
                    Err(Error::ImpreciseCastWouldLoseData)
738
                }
739
            }
740
24
            InnerInteger::U8(value) => Ok(*value as u32),
741
2
            InnerInteger::I16(value) => {
742
2
                if *value >= 0 {
743
1
                    Ok(*value as u32)
744
                } else {
745
1
                    Err(Error::ImpreciseCastWouldLoseData)
746
                }
747
            }
748
1
            InnerInteger::U16(value) => Ok(*value as u32),
749
14
            InnerInteger::U32(value) => Ok(*value),
750
4
            InnerInteger::I32(value) => {
751
4
                if *value >= 0 {
752
3
                    Ok(*value as u32)
753
                } else {
754
1
                    Err(Error::ImpreciseCastWouldLoseData)
755
                }
756
            }
757
            InnerInteger::U64(_)
758
            | InnerInteger::I64(_)
759
            | InnerInteger::U128(_)
760
6
            | InnerInteger::I128(_) => Err(Error::ImpreciseCastWouldLoseData),
761
        }
762
53
    }
763

            
764
    /// Returns the contained value as an i64, or an error if the value is unable to fit.
765
    #[allow(clippy::cast_possible_wrap)]
766
    #[allow(clippy::checked_conversions)]
767
    #[inline]
768
41
    pub const fn as_i64(&self) -> Result<i64, Error> {
769
41
        match &self.0 {
770
12
            InnerInteger::I8(value) => Ok(*value as i64),
771
2
            InnerInteger::U8(value) => Ok(*value as i64),
772
2
            InnerInteger::I16(value) => Ok(*value as i64),
773
2
            InnerInteger::U16(value) => Ok(*value as i64),
774
2
            InnerInteger::I32(value) => Ok(*value as i64),
775
4
            InnerInteger::U32(value) => Ok(*value as i64),
776
11
            InnerInteger::I64(value) => Ok(*value),
777
2
            InnerInteger::U64(value) => {
778
2
                if *value <= i64::MAX as u64 {
779
1
                    Ok(*value as i64)
780
                } else {
781
1
                    Err(Error::ImpreciseCastWouldLoseData)
782
                }
783
            }
784
4
            InnerInteger::U128(_) | InnerInteger::I128(_) => Err(Error::ImpreciseCastWouldLoseData),
785
        }
786
41
    }
787

            
788
    /// Returns the contained value as an i64, or an error if the value is unable to fit.
789
    #[allow(clippy::cast_possible_wrap)]
790
    #[inline]
791
81
    pub const fn as_i128(&self) -> Result<i128, Error> {
792
81
        match &self.0 {
793
18
            InnerInteger::I8(value) => Ok(*value as i128),
794
2
            InnerInteger::U8(value) => Ok(*value as i128),
795
10
            InnerInteger::I16(value) => Ok(*value as i128),
796
2
            InnerInteger::U16(value) => Ok(*value as i128),
797
18
            InnerInteger::I32(value) => Ok(*value as i128),
798
2
            InnerInteger::U32(value) => Ok(*value as i128),
799
16
            InnerInteger::I64(value) => Ok(*value as i128),
800
2
            InnerInteger::U64(value) => Ok(*value as i128),
801
9
            InnerInteger::I128(value) => Ok(*value),
802
2
            InnerInteger::U128(value) => {
803
2
                if *value <= i128::MAX as u128 {
804
1
                    Ok(*value as i128)
805
                } else {
806
1
                    Err(Error::ImpreciseCastWouldLoseData)
807
                }
808
            }
809
        }
810
81
    }
811

            
812
    /// Returns the contained value as an u64, or an error if the value is unable to fit.
813
    #[allow(clippy::cast_sign_loss)]
814
    #[inline]
815
10062
    pub const fn as_u64(&self) -> Result<u64, Error> {
816
10062
        match &self.0 {
817
2
            InnerInteger::I8(value) => {
818
2
                if *value >= 0 {
819
1
                    Ok(*value as u64)
820
                } else {
821
1
                    Err(Error::ImpreciseCastWouldLoseData)
822
                }
823
            }
824
37
            InnerInteger::U8(value) => Ok(*value as u64),
825
2
            InnerInteger::I16(value) => {
826
2
                if *value >= 0 {
827
1
                    Ok(*value as u64)
828
                } else {
829
1
                    Err(Error::ImpreciseCastWouldLoseData)
830
                }
831
            }
832
1
            InnerInteger::U16(value) => Ok(*value as u64),
833
1
            InnerInteger::U32(value) => Ok(*value as u64),
834
2
            InnerInteger::I32(value) => {
835
2
                if *value >= 0 {
836
1
                    Ok(*value as u64)
837
                } else {
838
1
                    Err(Error::ImpreciseCastWouldLoseData)
839
                }
840
            }
841
10012
            InnerInteger::U64(value) => Ok(*value),
842
2
            InnerInteger::I64(value) => {
843
2
                if *value >= 0 {
844
1
                    Ok(*value as u64)
845
                } else {
846
1
                    Err(Error::ImpreciseCastWouldLoseData)
847
                }
848
            }
849
3
            InnerInteger::U128(_) | InnerInteger::I128(_) => Err(Error::ImpreciseCastWouldLoseData),
850
        }
851
10062
    }
852

            
853
    /// Returns the contained value as an u64, or an error if the value is unable to fit.
854
    #[allow(clippy::cast_sign_loss)]
855
    #[inline]
856
56
    pub const fn as_u128(&self) -> Result<u128, Error> {
857
56
        match &self.0 {
858
2
            InnerInteger::I8(value) => {
859
2
                if *value >= 0 {
860
1
                    Ok(*value as u128)
861
                } else {
862
1
                    Err(Error::ImpreciseCastWouldLoseData)
863
                }
864
            }
865
15
            InnerInteger::U8(value) => Ok(*value as u128),
866
2
            InnerInteger::I16(value) => {
867
2
                if *value >= 0 {
868
1
                    Ok(*value as u128)
869
                } else {
870
1
                    Err(Error::ImpreciseCastWouldLoseData)
871
                }
872
            }
873
5
            InnerInteger::U16(value) => Ok(*value as u128),
874
9
            InnerInteger::U32(value) => Ok(*value as u128),
875
2
            InnerInteger::I32(value) => {
876
2
                if *value >= 0 {
877
1
                    Ok(*value as u128)
878
                } else {
879
1
                    Err(Error::ImpreciseCastWouldLoseData)
880
                }
881
            }
882
9
            InnerInteger::U64(value) => Ok(*value as u128),
883
2
            InnerInteger::I64(value) => {
884
2
                if *value >= 0 {
885
1
                    Ok(*value as u128)
886
                } else {
887
1
                    Err(Error::ImpreciseCastWouldLoseData)
888
                }
889
            }
890
8
            InnerInteger::U128(value) => Ok(*value),
891
2
            InnerInteger::I128(value) => {
892
2
                if *value >= 0 {
893
1
                    Ok(*value as u128)
894
                } else {
895
1
                    Err(Error::ImpreciseCastWouldLoseData)
896
                }
897
            }
898
        }
899
56
    }
900

            
901
    /// Writes this value using the smallest form possible.
902
    #[inline]
903
17
    pub fn write_to<W: WriteBytesExt>(&self, writer: W) -> std::io::Result<usize> {
904
17
        match self.0 {
905
3
            InnerInteger::I8(value) => write_i8(writer, value),
906
2
            InnerInteger::I16(value) => write_i16(writer, value),
907
2
            InnerInteger::I32(value) => write_i32(writer, value),
908
2
            InnerInteger::I64(value) => write_i64(writer, value),
909
2
            InnerInteger::I128(value) => write_i128(writer, value),
910
2
            InnerInteger::U8(value) => write_u8(writer, value),
911
1
            InnerInteger::U16(value) => write_u16(writer, value),
912
1
            InnerInteger::U32(value) => write_u32(writer, value),
913
1
            InnerInteger::U64(value) => write_u64(writer, value),
914
1
            InnerInteger::U128(value) => write_u128(writer, value),
915
        }
916
17
    }
917

            
918
    /// Reads an integer based on the atom header (`kind` and `byte_len`).
919
    /// `byte_len` should be the argument from the atom header directly.
920
    #[inline]
921
20314
    pub fn read_from<R: ReadBytesExt>(
922
20314
        kind: Kind,
923
20314
        byte_len: usize,
924
20314
        reader: &mut R,
925
20314
    ) -> Result<Self, Error> {
926
20314
        match kind {
927
137
            Kind::Int => match byte_len {
928
68
                1 => Ok(InnerInteger::I8(reader.read_i8()?)),
929
15
                2 => Ok(InnerInteger::I16(reader.read_i16::<LittleEndian>()?)),
930
8
                3 => Ok(InnerInteger::I32(reader.read_i24::<LittleEndian>()?)),
931
15
                4 => Ok(InnerInteger::I32(reader.read_i32::<LittleEndian>()?)),
932
8
                6 => Ok(InnerInteger::I64(reader.read_i48::<LittleEndian>()?)),
933
13
                8 => Ok(InnerInteger::I64(reader.read_i64::<LittleEndian>()?)),
934
9
                16 => Ok(InnerInteger::I128(reader.read_i128::<LittleEndian>()?)),
935
1
                count => Err(Error::UnsupportedByteCount(kind, count)),
936
            },
937
20176
            Kind::UInt => match byte_len {
938
153
                1 => Ok(InnerInteger::U8(reader.read_u8()?)),
939
9975
                2 => Ok(InnerInteger::U16(reader.read_u16::<LittleEndian>()?)),
940
9
                3 => Ok(InnerInteger::U32(reader.read_u24::<LittleEndian>()?)),
941
10
                4 => Ok(InnerInteger::U32(reader.read_u32::<LittleEndian>()?)),
942
4
                6 => Ok(InnerInteger::U64(reader.read_u48::<LittleEndian>()?)),
943
10016
                8 => Ok(InnerInteger::U64(reader.read_u64::<LittleEndian>()?)),
944
8
                16 => Ok(InnerInteger::U128(reader.read_u128::<LittleEndian>()?)),
945
1
                count => Err(Error::UnsupportedByteCount(kind, count)),
946
            },
947
1
            _ => Err(Error::UnexpectedKind(kind, Kind::Int)),
948
        }
949
20314
        .map(Integer)
950
20314
    }
951

            
952
    /// Converts this integer to an f32, but only if it can be done without losing precision.
953
    #[allow(clippy::cast_precision_loss)]
954
    #[inline]
955
8
    pub fn as_f32(&self) -> Result<f32, Error> {
956
8
        let int = self.as_i32()?;
957
6
        if int < -(2_i32.pow(f32::MANTISSA_DIGITS)) || int >= 2_i32.pow(f32::MANTISSA_DIGITS) {
958
2
            Err(Error::ImpreciseCastWouldLoseData)
959
        } else {
960
4
            Ok(int as f32)
961
        }
962
8
    }
963

            
964
    /// Converts this integer to an f64, but only if it can be done without losing precision.
965
    #[allow(clippy::cast_precision_loss)]
966
    #[inline]
967
6
    pub fn as_f64(&self) -> Result<f64, Error> {
968
6
        let int = self.as_i64()?;
969
6
        if int < -(2_i64.pow(f64::MANTISSA_DIGITS)) || int >= 2_i64.pow(f64::MANTISSA_DIGITS) {
970
2
            Err(Error::ImpreciseCastWouldLoseData)
971
        } else {
972
4
            Ok(int as f64)
973
        }
974
6
    }
975

            
976
    /// Converts this integer to an f64, but only if it can be done without losing precision.
977
    #[allow(clippy::cast_precision_loss)]
978
    #[inline]
979
4
    pub fn as_float(&self) -> Result<Float, Error> {
980
4
        self.as_f32()
981
4
            .map(Float::from)
982
4
            .or_else(|_| self.as_f64().map(Float::from))
983
4
    }
984
}
985

            
986
impl From<u8> for Integer {
987
    #[inline]
988
53
    fn from(value: u8) -> Self {
989
53
        Self(InnerInteger::U8(value))
990
53
    }
991
}
992

            
993
macro_rules! impl_from_unsigned_integer {
994
    ($primitive:ty, $smaller_primitive:ty, $variant:ident) => {
995
        impl From<$primitive> for Integer {
996
            #[inline]
997
            fn from(value: $primitive) -> Self {
998
136
                if let Ok(value) = <$smaller_primitive>::try_from(value) {
999
37
                    Self::from(value as $smaller_primitive)
                } else {
99
                    Integer(InnerInteger::$variant(value))
                }
136
            }
        }
    };
}

            
impl_from_unsigned_integer!(u16, u8, U16);
impl_from_unsigned_integer!(u32, u16, U32);
impl_from_unsigned_integer!(u64, u32, U64);
impl_from_unsigned_integer!(u128, u64, U128);

            
impl From<i8> for Integer {
    #[inline]
59
    fn from(value: i8) -> Self {
59
        Self(InnerInteger::I8(value))
59
    }
}

            
macro_rules! impl_from_unsigned_integer {
    ($primitive:ty, $smaller_primitive:ty, $smaller_unsigned_primitive:ty, $variant:ident) => {
        impl From<$primitive> for Integer {
            #[inline]
            fn from(value: $primitive) -> Self {
218
                if let Ok(value) = <$smaller_primitive>::try_from(value) {
80
                    Self::from(value as $smaller_primitive)
138
                } else if let Ok(value) = <$smaller_unsigned_primitive>::try_from(value) {
6
                    Self::from(value as $smaller_unsigned_primitive)
                } else {
132
                    Integer(InnerInteger::$variant(value))
                }
218
            }
        }
    };
}

            
impl_from_unsigned_integer!(i16, i8, u8, I16);
impl_from_unsigned_integer!(i32, i16, u16, I32);
impl_from_unsigned_integer!(i64, i32, u32, I64);
impl_from_unsigned_integer!(i128, i64, u64, I128);

            
/// Reads an atom.
#[allow(clippy::cast_possible_truncation)]
#[inline]
160759
pub fn read_atom<'de, R: Reader<'de>>(
160759
    reader: &mut R,
160759
    remaining_budget: &mut usize,
160759
    scratch: &mut Vec<u8>,
160759
) -> Result<Atom<'de>, Error> {
160759
    let (kind, arg) = read_atom_header(reader)?;
160759
    Ok(match kind {
90305
        Kind::Sequence | Kind::Map | Kind::Symbol => Atom {
90305
            kind,
90305
            arg,
90305
            nucleus: None,
90305
        },
        Kind::Special => Atom {
15125
            kind,
15125
            arg,
15125
            nucleus: match Special::try_from(arg)? {
5081
                Special::None => None,
20
                Special::Unit => Some(Nucleus::Unit),
1
                Special::False => Some(Nucleus::Boolean(false)),
3
                Special::True => Some(Nucleus::Boolean(true)),
10015
                Special::Named => Some(Nucleus::Named),
3
                Special::DynamicMap => Some(Nucleus::DynamicMap),
2
                Special::DynamicEnd => Some(Nucleus::DynamicEnd),
            },
        },
        Kind::Int | Kind::UInt => {
20296
            let bytes = arg as usize + 1;
20296
            update_budget(remaining_budget, in_memory_int_size(bytes))?;
            Atom {
20296
                kind,
20296
                arg,
20296
                nucleus: Some(Nucleus::Integer(Integer::read_from(kind, bytes, reader)?)),
            }
        }
        Kind::Float => {
43
            let bytes = arg as usize + 1;
43
            update_budget(remaining_budget, in_memory_int_size(bytes))?;
            Atom {
42
                kind,
42
                arg,
42
                nucleus: Some(Nucleus::Float(Float::read_from(kind, bytes, reader)?)),
            }
        }
        Kind::Bytes => {
34990
            let bytes = arg as usize;
34990
            update_budget(remaining_budget, bytes)?;
34988
            let bytes = reader.buffered_read_bytes(bytes, scratch)?;
34987
            Atom {
34987
                kind,
34987
                arg,
34987
                nucleus: Some(Nucleus::Bytes(bytes)),
34987
            }
        }
    })
160759
}

            
#[inline]
20339
pub(crate) const fn in_memory_int_size(encoded_length: usize) -> usize {
20339
    // Some integers are stored more compact than we can represent them in memory
20339
    match encoded_length {
17
        3 => 4,
12
        6 => 8,
20310
        other => other,
    }
20339
}

            
#[inline]
pub(crate) fn update_budget(budget: &mut usize, read_amount: usize) -> Result<(), Error> {
55329
    if let Some(remaining) = budget.checked_sub(read_amount) {
55326
        *budget = remaining;
55326
        Ok(())
    } else {
3
        Err(Error::TooManyBytesRead)
    }
55329
}

            
/// An encoded [`Kind`], argument, and optional contained value.
228
#[derive(Debug)]
pub struct Atom<'de> {
    /// The type of atom.
    pub kind: Kind,
    /// The argument contained in the atom header.
    pub arg: u64,
    /// The contained value, if any.
    pub nucleus: Option<Nucleus<'de>>,
}

            
/// A floating point number that can safely convert between other number types using compile-time evaluation when possible.
11
#[derive(Debug, Copy, Clone, PartialEq)]
pub struct Float(pub(crate) InnerFloat);

            
#[derive(Debug, Copy, Clone)]
pub(crate) enum InnerFloat {
    /// An f64 value.
    F64(f64),
    /// An f32 value.
    F32(f32),
}

            
impl From<f32> for Float {
    #[inline]
71
    fn from(value: f32) -> Self {
71
        Self(InnerFloat::F32(value))
71
    }
}

            
impl From<f64> for Float {
    #[inline]
24
    fn from(value: f64) -> Self {
24
        Self(InnerFloat::F64(value))
24
    }
}

            
impl PartialEq for InnerFloat {
    #[inline]
    fn eq(&self, other: &Self) -> bool {
11
        match (self, other) {
1
            (InnerFloat::F64(left), InnerFloat::F64(right)) => left == right,
8
            (InnerFloat::F32(left), InnerFloat::F32(right)) => left == right,
1
            (InnerFloat::F64(left), InnerFloat::F32(right)) => *left == f64::from(*right),
1
            (InnerFloat::F32(left), InnerFloat::F64(right)) => f64::from(*left) == *right,
        }
11
    }
}

            
impl Display for Float {
2
    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
2
        match &self.0 {
1
            InnerFloat::F32(value) => Display::fmt(value, f),
1
            InnerFloat::F64(value) => Display::fmt(value, f),
        }
2
    }
}

            
impl Float {
    /// Returns true if the value contained is zero.
    #[must_use]
    #[inline]
6
    pub fn is_zero(&self) -> bool {
6
        match self.0 {
4
            InnerFloat::F32(value) => value.abs() <= f32::EPSILON,
2
            InnerFloat::F64(value) => value.abs() <= f64::EPSILON,
        }
6
    }

            
    /// Returns this number as an f32, if it can be done without losing precision.
    #[allow(clippy::float_cmp, clippy::cast_possible_truncation)]
    #[inline]
22
    pub fn as_f32(&self) -> Result<f32, Error> {
22
        match self.0 {
20
            InnerFloat::F32(value) => Ok(value),
2
            InnerFloat::F64(value) => {
2
                let converted = value as f32;
2
                if f64::from(converted) == value {
1
                    Ok(converted)
                } else {
1
                    Err(Error::ImpreciseCastWouldLoseData)
                }
            }
        }
22
    }

            
    /// Returns this number as an f64.
    #[must_use]
    #[inline]
25
    pub const fn as_f64(&self) -> f64 {
25
        match self.0 {
5
            InnerFloat::F64(value) => value,
20
            InnerFloat::F32(value) => value as f64,
        }
25
    }

            
    /// Returns this number as an [`Integer`], if the stored value has no fractional part.
    #[allow(clippy::cast_possible_truncation)]
    #[inline]
20
    pub fn as_integer(&self) -> Result<Integer, Error> {
20
        match self.0 {
7
            InnerFloat::F64(value) => {
7
                if value.fract().abs() < f64::EPSILON {
                    // no fraction, safe to convert
6
                    Ok(Integer::from(value as i64))
                } else {
1
                    Err(Error::ImpreciseCastWouldLoseData)
                }
            }
13
            InnerFloat::F32(value) => {
13
                if value.fract().abs() < f32::EPSILON {
12
                    Ok(Integer::from(value as i32))
                } else {
1
                    Err(Error::ImpreciseCastWouldLoseData)
                }
            }
        }
20
    }

            
    /// Writes this value using the smallest form possible.
    #[inline]
2
    pub fn write_to<W: WriteBytesExt>(&self, writer: W) -> std::io::Result<usize> {
2
        match self.0 {
1
            InnerFloat::F64(float) => write_f64(writer, float),
1
            InnerFloat::F32(float) => write_f32(writer, float),
        }
2
    }

            
    /// Reads a floating point number given the atom `kind` and `byte_len`.
    /// `byte_len` should be the exact argument from the atom header.
    #[inline]
45
    pub fn read_from<R: ReadBytesExt>(
45
        kind: Kind,
45
        byte_len: usize,
45
        reader: &mut R,
45
    ) -> Result<Self, Error> {
45
        if Kind::Float == kind {
44
            match byte_len {
34
                2 => Ok(Self::from(read_f16(reader)?)),
5
                4 => Ok(Self::from(reader.read_f32::<LittleEndian>()?)),
4
                8 => Ok(Self::from(reader.read_f64::<LittleEndian>()?)),
1
                count => Err(Error::UnsupportedByteCount(Kind::Float, count)),
            }
        } else {
1
            Err(Error::UnexpectedKind(kind, Kind::Float))
        }
45
    }
}

            
/// A value contained within an [`Atom`].
13
#[derive(Debug)]
pub enum Nucleus<'de> {
    /// A boolean value.
    Boolean(bool),
    /// An integer value.
    Integer(Integer),
    /// A floating point value.
    Float(Float),
    /// A buffer of bytes.
    Bytes(BufferedBytes<'de>),
    /// A unit.
    Unit,
    /// A named value.
    Named,
    /// A marker denoting a map with unknown length is next in the file.
    DynamicMap,
    /// A marker denoting the end of a map with unknown length.
    DynamicEnd,
}

            
#[cfg(test)]
mod tests {
    use super::*;

            
    #[allow(clippy::cast_possible_truncation)]
17
    fn test_roundtrip_integer(input: Integer, expected: Integer, expected_size: usize) {
17
        let mut out = Vec::new();
17
        assert_eq!(input.write_to(&mut out).unwrap(), expected_size);
        {
17
            let mut reader = out.as_slice();
17
            let (kind, bytes) = read_atom_header(&mut reader).unwrap();
17
            assert_eq!(
17
                Integer::read_from(kind, bytes as usize + 1, &mut reader).unwrap(),
17
                expected
17
            );
        }
17
    }

            
    #[allow(clippy::cast_possible_truncation)]
2
    fn test_roundtrip_float(input: Float, expected: Float, expected_size: usize) {
2
        let mut out = Vec::new();
2
        assert_eq!(input.write_to(&mut out).unwrap(), expected_size);
        {
2
            let mut reader = out.as_slice();
2
            let (kind, bytes) = read_atom_header(&mut reader).unwrap();
2
            assert_eq!(
2
                Float::read_from(kind, bytes as usize + 1, &mut reader).unwrap(),
2
                expected
2
            );
        }
2
    }

            
1
    #[test]
1
    fn header() {
1
        let mut out = Vec::new();
1
        write_header(&mut out, 1).unwrap();
1
        let version = read_header(&mut out.as_slice()).unwrap();
1
        assert_eq!(version, 1);

            
1
        out[0] = 0;
1
        assert!(read_header(&mut out.as_slice()).is_err());
1
    }

            
1
    #[test]
1
    fn atom_header_args() {
1
        let mut out = Vec::new();
65
        for arg in 1..=64 {
64
            let arg = 2_u64.saturating_pow(arg);
64
            write_atom_header(&mut out, Kind::Map, arg).unwrap();
64
            println!("header: {out:?}");
64
            let (kind, read_arg) = read_atom_header(&mut out.as_slice()).unwrap();
64
            assert_eq!(kind, Kind::Map);
64
            assert_eq!(read_arg, arg);
64
            out.clear();
        }
1
    }

            
1
    #[test]
1
    fn atom_kinds() {
1
        assert_eq!(Kind::Special, Kind::from_u8(Kind::Special as u8).unwrap());
1
        assert_eq!(Kind::Int, Kind::from_u8(Kind::Int as u8).unwrap());
1
        assert_eq!(Kind::UInt, Kind::from_u8(Kind::UInt as u8).unwrap());
1
        assert_eq!(Kind::Float, Kind::from_u8(Kind::Float as u8).unwrap());
1
        assert_eq!(Kind::Sequence, Kind::from_u8(Kind::Sequence as u8).unwrap());
1
        assert_eq!(Kind::Map, Kind::from_u8(Kind::Map as u8).unwrap());
1
        assert_eq!(Kind::Symbol, Kind::from_u8(Kind::Symbol as u8).unwrap());
1
        assert_eq!(Kind::Bytes, Kind::from_u8(Kind::Bytes as u8).unwrap());
9
        for i in 8_u8..=15 {
8
            assert!(Kind::from_u8(i).is_err());
        }
1
    }

            
1
    #[test]
1
    fn zero() {
1
        test_roundtrip_integer(Integer::from(0_u64), Integer(InnerInteger::U8(0)), 2);
1
        test_roundtrip_integer(Integer::from(0_i64), Integer(InnerInteger::I8(0)), 2);
1
        test_roundtrip_float(Float::from(0_f32), Float(InnerFloat::F32(0.)), 3);
1
        test_roundtrip_float(Float::from(0_f64), Float(InnerFloat::F32(0.)), 3);
1
    }

            
1
    #[test]
1
    fn u8_max() {
1
        test_roundtrip_integer(
1
            Integer::from(u64::from(u8::MAX)),
1
            Integer(InnerInteger::U8(u8::MAX)),
1
            2,
1
        );
1
    }

            
1
    #[test]
1
    fn i8_max() {
1
        test_roundtrip_integer(
1
            Integer::from(i64::from(i8::MAX)),
1
            Integer(InnerInteger::I8(i8::MAX)),
1
            2,
1
        );
1
    }

            
1
    #[test]
1
    fn i8_min() {
1
        test_roundtrip_integer(
1
            Integer::from(i64::from(i8::MIN)),
1
            Integer(InnerInteger::I8(i8::MIN)),
1
            2,
1
        );
1
    }

            
1
    #[test]
1
    fn u16_max() {
1
        test_roundtrip_integer(
1
            Integer::from(u64::from(u16::MAX)),
1
            Integer(InnerInteger::U16(u16::MAX)),
1
            3,
1
        );
1
    }

            
1
    #[test]
1
    fn i16_max() {
1
        test_roundtrip_integer(
1
            Integer::from(i64::from(i16::MAX)),
1
            Integer(InnerInteger::I16(i16::MAX)),
1
            3,
1
        );
1
    }

            
1
    #[test]
1
    fn i16_min() {
1
        test_roundtrip_integer(
1
            Integer::from(i64::from(i16::MIN)),
1
            Integer(InnerInteger::I16(i16::MIN)),
1
            3,
1
        );
1
    }

            
1
    #[test]
1
    fn u32_max() {
1
        test_roundtrip_integer(
1
            Integer::from(u64::from(u32::MAX)),
1
            Integer(InnerInteger::U32(u32::MAX)),
1
            5,
1
        );
1
    }

            
1
    #[test]
1
    fn i32_max() {
1
        test_roundtrip_integer(
1
            Integer::from(i64::from(i32::MAX)),
1
            Integer(InnerInteger::I32(i32::MAX)),
1
            5,
1
        );
1
    }

            
1
    #[test]
1
    fn i32_min() {
1
        test_roundtrip_integer(
1
            Integer::from(i64::from(i32::MIN)),
1
            Integer(InnerInteger::I32(i32::MIN)),
1
            5,
1
        );
1
    }

            
1
    #[test]
1
    fn u64_max() {
1
        test_roundtrip_integer(
1
            Integer::from(u64::MAX),
1
            Integer(InnerInteger::U64(u64::MAX)),
1
            9,
1
        );
1
    }

            
1
    #[test]
1
    fn i64_max() {
1
        test_roundtrip_integer(
1
            Integer::from(i64::MAX),
1
            Integer(InnerInteger::I64(i64::MAX)),
1
            9,
1
        );
1
    }

            
1
    #[test]
1
    fn i64_min() {
1
        test_roundtrip_integer(
1
            Integer::from(i64::MIN),
1
            Integer(InnerInteger::I64(i64::MIN)),
1
            9,
1
        );
1
    }

            
1
    #[test]
1
    fn u128_max() {
1
        test_roundtrip_integer(
1
            Integer::from(u128::MAX),
1
            Integer(InnerInteger::U128(u128::MAX)),
1
            17,
1
        );
1
    }

            
1
    #[test]
1
    fn i128_max() {
1
        test_roundtrip_integer(
1
            Integer::from(i128::MAX),
1
            Integer(InnerInteger::I128(i128::MAX)),
1
            17,
1
        );
1
    }

            
1
    #[test]
1
    fn i128_min() {
1
        test_roundtrip_integer(
1
            Integer::from(i128::MIN),
1
            Integer(InnerInteger::I128(i128::MIN)),
1
            17,
1
        );
1
    }

            
1
    #[test]
1
    fn integer_is_zero() {
1
        assert!(Integer::from(0_i128).is_zero());
1
        assert!(!Integer::from(i8::MAX).is_zero());
1
        assert!(!Integer::from(i16::MAX).is_zero());
1
        assert!(!Integer::from(i32::MAX).is_zero());
1
        assert!(!Integer::from(i64::MAX).is_zero());
1
        assert!(!Integer::from(i128::MAX).is_zero());

            
1
        assert!(Integer::from(0_u128).is_zero());
1
        assert!(!Integer::from(u8::MAX).is_zero());
1
        assert!(!Integer::from(u16::MAX).is_zero());
1
        assert!(!Integer::from(u32::MAX).is_zero());
1
        assert!(!Integer::from(u64::MAX).is_zero());
1
        assert!(!Integer::from(u128::MAX).is_zero());
1
    }

            
    macro_rules! test_conversion_succeeds {
        ($host:ty, $value:expr, $method:ident) => {{
            assert!(<$host>::from($value).$method().is_ok())
        }};
    }
    macro_rules! test_conversion_fails {
        ($host:ty, $value:expr, $method:ident) => {{
            assert!(matches!(
                <$host>::from($value).$method(),
                Err(Error::ImpreciseCastWouldLoseData)
            ))
        }};
    }

            
1
    #[test]
    #[allow(clippy::cognitive_complexity, clippy::too_many_lines)]
1
    fn integer_casts() {
1
        macro_rules! test_negative_fails {
1
            ($method:ident) => {{
1
                test_conversion_fails!(Integer, i8::MIN, $method);
1
                test_conversion_fails!(Integer, i16::MIN, $method);
1
                test_conversion_fails!(Integer, i32::MIN, $method);
1
                test_conversion_fails!(Integer, i64::MIN, $method);
1
                test_conversion_fails!(Integer, i128::MIN, $method);
1
            }};
1
        }
1

            
1
        // ### i8 ###
1
        // unsigned max -> i8
1
        test_conversion_fails!(Integer, u8::MAX, as_i8);
1
        test_conversion_fails!(Integer, u16::MAX, as_i8);
1
        test_conversion_fails!(Integer, u32::MAX, as_i8);
1
        test_conversion_fails!(Integer, u64::MAX, as_i8);
1
        test_conversion_fails!(Integer, u128::MAX, as_i8);

            
        // signed max -> i8
1
        test_conversion_succeeds!(Integer, i8::MAX, as_i8);
1
        test_conversion_fails!(Integer, i16::MAX, as_i8);
1
        test_conversion_fails!(Integer, i32::MAX, as_i8);
1
        test_conversion_fails!(Integer, i64::MAX, as_i8);
1
        test_conversion_fails!(Integer, i128::MAX, as_i8);

            
        // signed max as unsigned -> i8
1
        test_conversion_succeeds!(Integer, i8::MAX as u8, as_i8);
1
        test_conversion_fails!(Integer, i16::MAX as u16, as_i8);
1
        test_conversion_fails!(Integer, i32::MAX as u32, as_i8);
1
        test_conversion_fails!(Integer, i64::MAX as u64, as_i8);
1
        test_conversion_fails!(Integer, i128::MAX as u128, as_i8);

            
        // signed min -> i8
1
        test_conversion_succeeds!(Integer, i8::MIN, as_i8);
1
        test_conversion_fails!(Integer, i16::MIN, as_i8);
1
        test_conversion_fails!(Integer, i32::MIN, as_i8);
1
        test_conversion_fails!(Integer, i64::MIN, as_i8);
1
        test_conversion_fails!(Integer, i128::MIN, as_i8);

            
        // ### i16 ###
        // unsigned max -> i16
1
        test_conversion_succeeds!(Integer, u8::MAX, as_i16);
1
        test_conversion_fails!(Integer, u16::MAX, as_i16);
1
        test_conversion_fails!(Integer, u32::MAX, as_i16);
1
        test_conversion_fails!(Integer, u64::MAX, as_i16);
1
        test_conversion_fails!(Integer, u128::MAX, as_i16);

            
        // signed max -> i16
1
        test_conversion_succeeds!(Integer, i8::MAX, as_i16);
1
        test_conversion_succeeds!(Integer, i16::MAX, as_i16);
1
        test_conversion_fails!(Integer, i32::MAX, as_i16);
1
        test_conversion_fails!(Integer, i64::MAX, as_i16);
1
        test_conversion_fails!(Integer, i128::MAX, as_i16);

            
        // signed max as unsigned -> i16
1
        test_conversion_succeeds!(Integer, i8::MAX as u8, as_i16);
1
        test_conversion_succeeds!(Integer, i16::MAX as u16, as_i16);
1
        test_conversion_fails!(Integer, i32::MAX as u32, as_i16);
1
        test_conversion_fails!(Integer, i64::MAX as u64, as_i16);
1
        test_conversion_fails!(Integer, i128::MAX as u128, as_i16);

            
        // signed min -> i16
1
        test_conversion_succeeds!(Integer, i8::MIN, as_i16);
1
        test_conversion_succeeds!(Integer, i16::MIN, as_i16);
1
        test_conversion_fails!(Integer, i32::MIN, as_i16);
1
        test_conversion_fails!(Integer, i64::MIN, as_i16);
1
        test_conversion_fails!(Integer, i128::MIN, as_i16);

            
        // ### i32 ###
        // unsigned max -> i32
1
        test_conversion_succeeds!(Integer, u8::MAX, as_i32);
1
        test_conversion_succeeds!(Integer, u16::MAX, as_i32);
1
        test_conversion_fails!(Integer, u32::MAX, as_i32);
1
        test_conversion_fails!(Integer, u64::MAX, as_i32);
1
        test_conversion_fails!(Integer, u128::MAX, as_i32);

            
        // signed max -> i32
1
        test_conversion_succeeds!(Integer, i8::MAX, as_i32);
1
        test_conversion_succeeds!(Integer, i16::MAX, as_i32);
1
        test_conversion_succeeds!(Integer, i32::MAX, as_i32);
1
        test_conversion_fails!(Integer, i64::MAX, as_i32);
1
        test_conversion_fails!(Integer, i128::MAX, as_i32);

            
        // signed max as unsigned -> i32
1
        test_conversion_succeeds!(Integer, i8::MAX as u8, as_i32);
1
        test_conversion_succeeds!(Integer, i16::MAX as u16, as_i32);
1
        test_conversion_succeeds!(Integer, i32::MAX as u32, as_i32);
1
        test_conversion_fails!(Integer, i64::MAX as u64, as_i32);
1
        test_conversion_fails!(Integer, i128::MAX as u128, as_i32);

            
        // signed min -> i32
1
        test_conversion_succeeds!(Integer, i8::MIN, as_i32);
1
        test_conversion_succeeds!(Integer, i16::MIN, as_i32);
1
        test_conversion_succeeds!(Integer, i32::MIN, as_i32);
1
        test_conversion_fails!(Integer, i64::MIN, as_i32);
1
        test_conversion_fails!(Integer, i128::MIN, as_i32);

            
        // ### i64 ###
        // unsigned max -> i64
1
        test_conversion_succeeds!(Integer, u8::MAX, as_i64);
1
        test_conversion_succeeds!(Integer, u16::MAX, as_i64);
1
        test_conversion_succeeds!(Integer, u32::MAX, as_i64);
1
        test_conversion_fails!(Integer, u64::MAX, as_i64);
1
        test_conversion_fails!(Integer, u128::MAX, as_i64);

            
        // signed max -> i64
1
        test_conversion_succeeds!(Integer, i8::MAX, as_i64);
1
        test_conversion_succeeds!(Integer, i16::MAX, as_i64);
1
        test_conversion_succeeds!(Integer, i32::MAX, as_i64);
1
        test_conversion_succeeds!(Integer, i64::MAX, as_i64);
1
        test_conversion_fails!(Integer, i128::MAX, as_i64);

            
        // signed max as unsigned -> i64
1
        test_conversion_succeeds!(Integer, i8::MAX as u8, as_i64);
1
        test_conversion_succeeds!(Integer, i16::MAX as u16, as_i64);
1
        test_conversion_succeeds!(Integer, i32::MAX as u32, as_i64);
1
        test_conversion_succeeds!(Integer, i64::MAX as u64, as_i64);
1
        test_conversion_fails!(Integer, i128::MAX as u128, as_i64);

            
        // signed min -> i64
1
        test_conversion_succeeds!(Integer, i8::MIN, as_i64);
1
        test_conversion_succeeds!(Integer, i16::MIN, as_i64);
1
        test_conversion_succeeds!(Integer, i32::MIN, as_i64);
1
        test_conversion_succeeds!(Integer, i64::MIN, as_i64);
1
        test_conversion_fails!(Integer, i128::MIN, as_i64);

            
        // ### i128 ###
        // unsigned max -> i128
1
        test_conversion_succeeds!(Integer, u8::MAX, as_i128);
1
        test_conversion_succeeds!(Integer, u16::MAX, as_i128);
1
        test_conversion_succeeds!(Integer, u32::MAX, as_i128);
1
        test_conversion_succeeds!(Integer, u64::MAX, as_i128);
1
        test_conversion_fails!(Integer, u128::MAX, as_i128);

            
        // signed max -> i128
1
        test_conversion_succeeds!(Integer, i8::MAX, as_i128);
1
        test_conversion_succeeds!(Integer, i16::MAX, as_i128);
1
        test_conversion_succeeds!(Integer, i32::MAX, as_i128);
1
        test_conversion_succeeds!(Integer, i64::MAX, as_i128);
1
        test_conversion_succeeds!(Integer, i128::MAX, as_i128);

            
        // signed max as unsigned -> i128
1
        test_conversion_succeeds!(Integer, i8::MAX as u8, as_i128);
1
        test_conversion_succeeds!(Integer, i16::MAX as u16, as_i128);
1
        test_conversion_succeeds!(Integer, i32::MAX as u32, as_i128);
1
        test_conversion_succeeds!(Integer, i64::MAX as u64, as_i128);
1
        test_conversion_succeeds!(Integer, i128::MAX as u128, as_i128);

            
        // signed min -> i128
1
        test_conversion_succeeds!(Integer, i8::MIN, as_i128);
1
        test_conversion_succeeds!(Integer, i16::MIN, as_i128);
1
        test_conversion_succeeds!(Integer, i32::MIN, as_i128);
1
        test_conversion_succeeds!(Integer, i64::MIN, as_i128);
1
        test_conversion_succeeds!(Integer, i128::MIN, as_i128);

            
        // ### u8 ###
        // unsigned max -> u8
1
        test_conversion_succeeds!(Integer, u8::MAX, as_u8);
1
        test_conversion_fails!(Integer, u16::MAX, as_u8);
1
        test_conversion_fails!(Integer, u32::MAX, as_u8);
1
        test_conversion_fails!(Integer, u64::MAX, as_u8);
1
        test_conversion_fails!(Integer, u128::MAX, as_u8);

            
        // signed max -> u8
1
        test_conversion_succeeds!(Integer, i8::MAX, as_u8);
1
        test_conversion_fails!(Integer, i16::MAX, as_u8);
1
        test_conversion_fails!(Integer, i32::MAX, as_u8);
1
        test_conversion_fails!(Integer, i64::MAX, as_u8);
1
        test_conversion_fails!(Integer, i128::MAX, as_u8);

            
        // signed min -> u8
1
        test_negative_fails!(as_u8);

            
        // ### u16 ###
        // unsigned max -> u16
1
        test_conversion_succeeds!(Integer, u8::MAX, as_u16);
1
        test_conversion_succeeds!(Integer, u16::MAX, as_u16);
1
        test_conversion_fails!(Integer, u32::MAX, as_u16);
1
        test_conversion_fails!(Integer, u64::MAX, as_u16);
1
        test_conversion_fails!(Integer, u128::MAX, as_u16);

            
        // signed max -> u16
1
        test_conversion_succeeds!(Integer, i8::MAX, as_u16);
1
        test_conversion_succeeds!(Integer, i16::MAX, as_u16);
1
        test_conversion_fails!(Integer, i32::MAX, as_u16);
1
        test_conversion_fails!(Integer, i64::MAX, as_u16);
1
        test_conversion_fails!(Integer, i128::MAX, as_u16);

            
        // signed min -> u16
1
        test_negative_fails!(as_u16);

            
        // ### u32 ###
        // unsigned max -> u32
1
        test_conversion_succeeds!(Integer, u8::MAX, as_u32);
1
        test_conversion_succeeds!(Integer, u16::MAX, as_u32);
1
        test_conversion_succeeds!(Integer, u32::MAX, as_u32);
1
        test_conversion_fails!(Integer, u64::MAX, as_u32);
1
        test_conversion_fails!(Integer, u128::MAX, as_u32);

            
        // signed max -> u32
1
        test_conversion_succeeds!(Integer, i8::MAX, as_u32);
1
        test_conversion_succeeds!(Integer, i16::MAX, as_u32);
1
        test_conversion_succeeds!(Integer, i32::MAX, as_u32);
1
        test_conversion_fails!(Integer, i64::MAX, as_u32);
1
        test_conversion_fails!(Integer, i128::MAX, as_u32);

            
        // signed min -> u32
1
        test_negative_fails!(as_u32);

            
        // ### u64 ###
        // unsigned max -> u64
1
        test_conversion_succeeds!(Integer, u8::MAX, as_u64);
1
        test_conversion_succeeds!(Integer, u16::MAX, as_u64);
1
        test_conversion_succeeds!(Integer, u32::MAX, as_u64);
1
        test_conversion_succeeds!(Integer, u64::MAX, as_u64);
1
        test_conversion_fails!(Integer, u128::MAX, as_u64);

            
        // signed max -> u64
1
        test_conversion_succeeds!(Integer, i8::MAX, as_u64);
1
        test_conversion_succeeds!(Integer, i16::MAX, as_u64);
1
        test_conversion_succeeds!(Integer, i32::MAX, as_u64);
1
        test_conversion_succeeds!(Integer, i64::MAX, as_u64);
1
        test_conversion_fails!(Integer, i128::MAX, as_u64);

            
        // signed min -> u64
1
        test_negative_fails!(as_u64);

            
        // ### u128 ###
        // unsigned max -> u128
1
        test_conversion_succeeds!(Integer, u8::MAX, as_u128);
1
        test_conversion_succeeds!(Integer, u16::MAX, as_u128);
1
        test_conversion_succeeds!(Integer, u32::MAX, as_u128);
1
        test_conversion_succeeds!(Integer, u64::MAX, as_u128);
1
        test_conversion_succeeds!(Integer, u128::MAX, as_u128);

            
        // signed max -> u128
1
        test_conversion_succeeds!(Integer, i8::MAX, as_u128);
1
        test_conversion_succeeds!(Integer, i16::MAX, as_u128);
1
        test_conversion_succeeds!(Integer, i32::MAX, as_u128);
1
        test_conversion_succeeds!(Integer, i64::MAX, as_u128);
1
        test_conversion_succeeds!(Integer, i128::MAX, as_u128);

            
        // signed min -> u128
1
        test_negative_fails!(as_u128);
1
    }

            
1
    #[test]
1
    fn float_as_integer() {
1
        test_conversion_succeeds!(Float, 1_f32, as_integer);
1
        test_conversion_succeeds!(Float, 1_f64, as_integer);
1
        test_conversion_fails!(Float, 1.1_f32, as_integer);
1
        test_conversion_fails!(Float, 1.1_f64, as_integer);
1
    }

            
1
    #[test]
1
    fn integer_as_float() {
1
        test_conversion_succeeds!(Integer, -(2_i32.pow(f32::MANTISSA_DIGITS)), as_f32);
1
        test_conversion_succeeds!(Integer, 2_i32.pow(f32::MANTISSA_DIGITS) - 1, as_f32);
1
        test_conversion_fails!(Integer, i32::MIN, as_f32);
1
        test_conversion_fails!(Integer, i32::MAX, as_f32);
1
        test_conversion_succeeds!(Integer, -(2_i64.pow(f64::MANTISSA_DIGITS)), as_f64);
1
        test_conversion_succeeds!(Integer, 2_i64.pow(f64::MANTISSA_DIGITS) - 1, as_f64);
1
        test_conversion_fails!(Integer, i64::MIN, as_f64);
1
        test_conversion_fails!(Integer, i64::MAX, as_f64);
1
    }

            
1
    #[test]
1
    fn float_partial_eqs() {
1
        assert_eq!(Float::from(0_f32), Float::from(0_f32));
1
        assert_eq!(Float::from(0_f32), Float::from(0_f64));
1
        assert_eq!(Float::from(0_f64), Float::from(0_f32));
1
    }

            
1
    #[test]
1
    fn float_is_zero() {
1
        assert!(Float::from(0_f32).is_zero());
1
        assert!(!Float::from(1_f32).is_zero());
1
        assert!(Float::from(0_f64).is_zero());
1
        assert!(!Float::from(1_f64).is_zero());
1
    }

            
1
    #[test]
1
    fn integer_display() {
1
        assert_eq!(Integer::from(i8::MAX).to_string(), "127");
1
        assert_eq!(Integer::from(i16::MAX).to_string(), "32767");
1
        assert_eq!(Integer::from(i32::MAX).to_string(), "2147483647");
1
        assert_eq!(Integer::from(i64::MAX).to_string(), "9223372036854775807");
1
        assert_eq!(
1
            Integer::from(i128::MAX).to_string(),
1
            "170141183460469231731687303715884105727"
1
        );
1
        assert_eq!(Integer::from(u8::MAX).to_string(), "255");
1
        assert_eq!(Integer::from(u16::MAX).to_string(), "65535");
1
        assert_eq!(Integer::from(u32::MAX).to_string(), "4294967295");
1
        assert_eq!(Integer::from(u64::MAX).to_string(), "18446744073709551615");
1
        assert_eq!(
1
            Integer::from(u128::MAX).to_string(),
1
            "340282366920938463463374607431768211455"
1
        );
1
    }
}