1
use std::fmt::Display;
2

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

            
6
pub(crate) const INITIAL_VERSION: u8 = 0;
7
pub(crate) const V4_VERSION: u8 = 1;
8
pub(crate) const CURRENT_VERSION: u8 = V4_VERSION;
9

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

            
27
1659908
    writer.write_all(&[first_byte])?;
28
1659908
    Ok(1)
29
1659908
}
30

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

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

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

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

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

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

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

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

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

            
145
160854
    Ok((kind, arg))
146
160854
}
147

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

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

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

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

            
222
impl TryFrom<u64> for Special {
223
    type Error = UnknownSpecial;
224

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

            
995
macro_rules! impl_from_unsigned_integer {
996
    ($primitive:ty, $smaller_primitive:ty, $variant:ident) => {
997
        impl From<$primitive> for Integer {
998
            #[inline]
999
136
            fn from(value: $primitive) -> Self {
136
                if let Ok(value) = <$smaller_primitive>::try_from(value) {
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]
218
            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]
160771
pub fn read_atom<'de, R: Reader<'de>>(
160771
    reader: &mut R,
160771
    remaining_budget: &mut usize,
160771
    scratch: &mut Vec<u8>,
160771
) -> Result<Atom<'de>, Error> {
160771
    let (kind, arg) = read_atom_header(reader)?;
160771
    Ok(match kind {
90311
        Kind::Sequence | Kind::Map | Kind::Symbol => Atom {
90311
            kind,
90311
            arg,
90311
            nucleus: None,
90311
        },
        Kind::Special => Atom {
15081
            kind,
15081
            arg,
15081
            nucleus: match Special::try_from(arg)? {
5034
                Special::None => None,
20
                Special::Unit => Some(Nucleus::Unit),
1
                Special::False => Some(Nucleus::Boolean(false)),
3
                Special::True => Some(Nucleus::Boolean(true)),
10018
                Special::Named => Some(Nucleus::Named),
3
                Special::DynamicMap => Some(Nucleus::DynamicMap),
2
                Special::DynamicEnd => Some(Nucleus::DynamicEnd),
            },
        },
        Kind::Int | Kind::UInt => {
20298
            let bytes = arg as usize + 1;
20298
            update_budget(remaining_budget, in_memory_int_size(bytes))?;
            Atom {
20298
                kind,
20298
                arg,
20298
                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 => {
35038
            let bytes = arg as usize;
35038
            update_budget(remaining_budget, bytes)?;
35036
            let bytes = reader.buffered_read_bytes(bytes, scratch)?;
35035
            Atom {
35035
                kind,
35035
                arg,
35035
                nucleus: Some(Nucleus::Bytes(bytes)),
35035
            }
        }
    })
160771
}

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

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

            
/// An encoded [`Kind`], argument, and optional contained value.
#[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.
#[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]
11
    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`].
#[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
    }

            
    #[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
    }

            
    #[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
    }

            
    #[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
    }

            
    #[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
    }

            
    #[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
    }

            
    #[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
    }

            
    #[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
    }

            
    #[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
    }

            
    #[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
    }

            
    #[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
    }

            
    #[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
    }

            
    #[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
    }

            
    #[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
    }

            
    #[test]
1
    fn u64_max() {
1
        test_roundtrip_integer(
1
            Integer::from(u64::MAX),
1
            Integer(InnerInteger::U64(u64::MAX)),
1
            9,
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
    }

            
    #[test]
1
    fn i64_min() {
1
        test_roundtrip_integer(
1
            Integer::from(i64::MIN),
1
            Integer(InnerInteger::I64(i64::MIN)),
1
            9,
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
    }

            
    #[test]
1
    fn i128_max() {
1
        test_roundtrip_integer(
1
            Integer::from(i128::MAX),
1
            Integer(InnerInteger::I128(i128::MAX)),
1
            17,
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
    }

            
    #[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)
            ))
        }};
    }

            
    #[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
    }

            
    #[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
    }

            
    #[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
    }

            
    #[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
    }

            
    #[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
    }

            
    #[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
    }
}