1use crate::ieee_soft::ExactNumArray;
10use crate::ExactNum;
11use crate::Exponent;
12use crate::Sign;
13use crate::Word;
14use crate::INF_NEG;
15use crate::INF_POS;
16use crate::WORD_BIT_SIZE;
17use crate::{Error, NAN};
18use alloc::vec::Vec;
19
20pub const BINARY_FORMAT_VERSION: u8 = 1;
22
23pub const BINARY_INLINE_LEN: usize = 16;
25
26pub const BINARY_INLINE_MANT_BITS: usize = 64;
28
29pub const BINARY_INLINE_U32_WORDS: usize = 2;
31
32pub const BINARY_HEADER_LEN: usize = 16;
34
35pub const BINARY_MAX_U32: usize = 65_536;
37
38pub const BINARY_MAX_ELEMS: usize = 1_048_576;
40
41pub const BINARY_FLAG_POS: u8 = 0x00;
43pub const BINARY_FLAG_NEG: u8 = 0x01;
45pub const BINARY_FLAG_INF_POS: u8 = 0x02;
47pub const BINARY_FLAG_INF_NEG: u8 = 0x03;
49pub const BINARY_FLAG_NAN_DIV0: u8 = 0x04;
51pub const BINARY_FLAG_NAN_INVALID: u8 = 0x05;
53pub const BINARY_FLAG_NAN_RETRY: u8 = 0x06;
55pub const BINARY_FLAG_NAN_OOM: u8 = 0x07;
57pub const BINARY_FLAG_NAN_OVF_POS: u8 = 0x08;
59pub const BINARY_FLAG_NAN_OVF_NEG: u8 = 0x09;
61pub const BINARY_FLAG_NAN_BARE: u8 = 0x0A;
63pub const BINARY_FLAG_HEAP_POS: u8 = 0x10;
65pub const BINARY_FLAG_HEAP_NEG: u8 = 0x11;
67pub const BINARY_FLAG_ARRAY: u8 = 0x20;
69
70const U32_BYTES: usize = 4;
71const WORD_U32: usize = WORD_BIT_SIZE / 32;
72
73#[derive(Clone, Copy, Debug, PartialEq, Eq)]
75pub struct InlineBinaryBuffer {
76 bytes: [u8; BINARY_INLINE_LEN],
77}
78
79impl InlineBinaryBuffer {
80 pub fn as_bytes(&self) -> &[u8; BINARY_INLINE_LEN] {
82 &self.bytes
83 }
84
85 pub fn into_bytes(self) -> [u8; BINARY_INLINE_LEN] {
87 self.bytes
88 }
89}
90
91impl ExactNum {
92 pub fn to_inline_bytes(&self) -> Result<InlineBinaryBuffer, Error> {
97 let mut bytes = [0u8; BINARY_INLINE_LEN];
98 write_inline(self, &mut bytes)?;
99 Ok(InlineBinaryBuffer { bytes })
100 }
101
102 pub fn write_inline_bytes(&self, dest: &mut [u8]) -> Result<usize, Error> {
108 if dest.len() < BINARY_INLINE_LEN {
109 return Err(Error::InvalidArgument);
110 }
111 write_inline(self, dest)?;
112 Ok(BINARY_INLINE_LEN)
113 }
114
115 pub fn from_inline_bytes(bytes: &[u8; BINARY_INLINE_LEN]) -> Result<Self, Error> {
117 let (v, n) = decode_num(bytes)?;
118 if n != BINARY_INLINE_LEN {
119 return Err(Error::InvalidArgument);
120 }
121 Ok(v)
122 }
123
124 pub fn to_bytes(&self) -> Result<Vec<u8>, Error> {
126 let n = encoded_len(self)?;
127 let mut out = Vec::new();
128 out.try_reserve_exact(n)?;
129 out.resize(n, 0);
130 let wrote = write_num(self, &mut out)?;
131 if wrote != n {
132 return Err(Error::InvalidArgument);
133 }
134 Ok(out)
135 }
136
137 pub fn write_bytes(&self, dest: &mut [u8]) -> Result<usize, Error> {
139 write_num(self, dest)
140 }
141
142 pub fn from_bytes(bytes: &[u8]) -> Result<Self, Error> {
144 let (v, n) = decode_num(bytes)?;
145 if n != bytes.len() {
146 return Err(Error::InvalidArgument);
147 }
148 Ok(v)
149 }
150}
151
152impl ExactNumArray {
153 pub fn to_bytes(&self) -> Result<Vec<u8>, Error> {
155 let n = encoded_array_len(self)?;
156 let mut out = Vec::new();
157 out.try_reserve_exact(n)?;
158 out.resize(n, 0);
159 let wrote = write_array(self, &mut out)?;
160 if wrote != n {
161 return Err(Error::InvalidArgument);
162 }
163 Ok(out)
164 }
165
166 pub fn write_bytes(&self, dest: &mut [u8]) -> Result<usize, Error> {
168 write_array(self, dest)
169 }
170
171 pub fn from_bytes(bytes: &[u8]) -> Result<Self, Error> {
173 decode_array(bytes)
174 }
175}
176
177fn flag_nan(err: Option<Error>) -> u8 {
178 match err {
179 Some(Error::DivisionByZero) => BINARY_FLAG_NAN_DIV0,
180 Some(Error::InvalidArgument) => BINARY_FLAG_NAN_INVALID,
181 Some(Error::PrecisionRetryExhausted) => BINARY_FLAG_NAN_RETRY,
182 Some(Error::MemoryAllocation) => BINARY_FLAG_NAN_OOM,
183 Some(Error::ExponentOverflow(Sign::Pos)) => BINARY_FLAG_NAN_OVF_POS,
184 Some(Error::ExponentOverflow(Sign::Neg)) => BINARY_FLAG_NAN_OVF_NEG,
185 None => BINARY_FLAG_NAN_BARE,
186 }
187}
188
189fn nan_from_flag(flag: u8) -> Result<ExactNum, Error> {
190 match flag {
191 BINARY_FLAG_NAN_DIV0 => Ok(ExactNum::nan(Some(Error::DivisionByZero))),
192 BINARY_FLAG_NAN_INVALID => Ok(ExactNum::nan(Some(Error::InvalidArgument))),
193 BINARY_FLAG_NAN_RETRY => Ok(ExactNum::nan(Some(Error::PrecisionRetryExhausted))),
194 BINARY_FLAG_NAN_OOM => Ok(ExactNum::nan(Some(Error::MemoryAllocation))),
195 BINARY_FLAG_NAN_OVF_POS => Ok(ExactNum::nan(Some(Error::ExponentOverflow(Sign::Pos)))),
196 BINARY_FLAG_NAN_OVF_NEG => Ok(ExactNum::nan(Some(Error::ExponentOverflow(Sign::Neg)))),
197 BINARY_FLAG_NAN_BARE => Ok(NAN.clone()),
198 _ => Err(Error::InvalidArgument),
199 }
200}
201
202fn is_special_flag(flag: u8) -> bool {
203 matches!(
204 flag,
205 BINARY_FLAG_INF_POS
206 | BINARY_FLAG_INF_NEG
207 | BINARY_FLAG_NAN_DIV0
208 | BINARY_FLAG_NAN_INVALID
209 | BINARY_FLAG_NAN_RETRY
210 | BINARY_FLAG_NAN_OOM
211 | BINARY_FLAG_NAN_OVF_POS
212 | BINARY_FLAG_NAN_OVF_NEG
213 | BINARY_FLAG_NAN_BARE
214 )
215}
216
217fn write_special(flag: u8, dest: &mut [u8]) -> Result<usize, Error> {
218 if dest.len() < BINARY_INLINE_LEN {
219 return Err(Error::InvalidArgument);
220 }
221 dest[..BINARY_INLINE_LEN].fill(0);
222 dest[0] = flag;
223 dest[1] = BINARY_FORMAT_VERSION;
224 Ok(BINARY_INLINE_LEN)
225}
226
227fn write_inline(x: &ExactNum, dest: &mut [u8]) -> Result<usize, Error> {
228 if dest.len() < BINARY_INLINE_LEN {
229 return Err(Error::InvalidArgument);
230 }
231 if x.is_nan() {
232 return write_special(flag_nan(x.err()), dest);
233 }
234 if x.is_inf_pos() {
235 return write_special(BINARY_FLAG_INF_POS, dest);
236 }
237 if x.is_inf_neg() {
238 return write_special(BINARY_FLAG_INF_NEG, dest);
239 }
240 let Some((m, n_sig, sign, exp, inexact)) = x.as_raw_parts() else {
241 return Err(Error::InvalidArgument);
242 };
243 let n_u32 = u32_count(m.len());
244 if n_u32 > BINARY_INLINE_U32_WORDS {
245 return Err(Error::MemoryAllocation);
246 }
247 dest[..BINARY_INLINE_LEN].fill(0);
248 dest[0] = if sign == Sign::Neg { BINARY_FLAG_NEG } else { BINARY_FLAG_POS };
249 dest[1] = BINARY_FORMAT_VERSION;
250 dest[2] = u8::try_from(n_sig).map_err(|_| Error::InvalidArgument)?;
251 dest[3] = u8::from(inexact);
252 dest[4..8].copy_from_slice(&exp.to_be_bytes());
253 let mut tmp = [0u32; BINARY_INLINE_U32_WORDS];
254 words_to_u32(m, &mut tmp)?;
255 put_u32_be(&mut dest[8..12], tmp[0]);
256 put_u32_be(&mut dest[12..16], tmp[1]);
257 Ok(BINARY_INLINE_LEN)
258}
259
260fn encoded_len(x: &ExactNum) -> Result<usize, Error> {
261 if x.is_nan() || x.is_inf() {
262 return Ok(BINARY_INLINE_LEN);
263 }
264 let Some((m, _, _, _, _)) = x.as_raw_parts() else {
265 return Err(Error::InvalidArgument);
266 };
267 let n_u32 = u32_count(m.len());
268 if n_u32 <= BINARY_INLINE_U32_WORDS {
269 Ok(BINARY_INLINE_LEN)
270 } else {
271 heap_len(n_u32)
272 }
273}
274
275fn heap_len(n_u32: usize) -> Result<usize, Error> {
276 if n_u32 > BINARY_MAX_U32 {
277 return Err(Error::InvalidArgument);
278 }
279 n_u32
280 .checked_mul(U32_BYTES)
281 .and_then(|b| b.checked_add(BINARY_HEADER_LEN))
282 .ok_or(Error::InvalidArgument)
283}
284
285fn write_num(x: &ExactNum, dest: &mut [u8]) -> Result<usize, Error> {
286 if x.is_nan() || x.is_inf() {
287 return write_inline(x, dest);
288 }
289 let Some((m, n_sig, sign, exp, inexact)) = x.as_raw_parts() else {
290 return Err(Error::InvalidArgument);
291 };
292 let n_u32 = u32_count(m.len());
293 if n_u32 <= BINARY_INLINE_U32_WORDS {
294 return write_inline(x, dest);
295 }
296 let need = heap_len(n_u32)?;
297 if dest.len() < need {
298 return Err(Error::InvalidArgument);
299 }
300 dest[..need].fill(0);
301 dest[0] = if sign == Sign::Neg { BINARY_FLAG_HEAP_NEG } else { BINARY_FLAG_HEAP_POS };
302 dest[1] = BINARY_FORMAT_VERSION;
303 dest[3] = u8::from(inexact);
304 dest[4..8].copy_from_slice(&exp.to_be_bytes());
305 put_u32_be(&mut dest[8..12], u32_from_usize(n_sig)?);
306 put_u32_be(&mut dest[12..16], u32_from_usize(n_u32)?);
307 let mut limb = [0u32; 2];
308 let mut off = BINARY_HEADER_LEN;
309 for &w in m {
310 words_to_u32(core::slice::from_ref(&w), &mut limb[..WORD_U32])?;
311 for &u in limb.iter().take(WORD_U32) {
312 put_u32_be(&mut dest[off..off + U32_BYTES], u);
313 off += U32_BYTES;
314 }
315 }
316 Ok(need)
317}
318
319fn decode_num(bytes: &[u8]) -> Result<(ExactNum, usize), Error> {
320 if bytes.len() < 2 {
321 return Err(Error::InvalidArgument);
322 }
323 let flag = bytes[0];
324 if bytes[1] != BINARY_FORMAT_VERSION {
325 return Err(Error::InvalidArgument);
326 }
327 if flag == BINARY_FLAG_ARRAY {
328 return Err(Error::InvalidArgument);
329 }
330 if is_special_flag(flag) {
331 if bytes.len() < BINARY_INLINE_LEN {
332 return Err(Error::InvalidArgument);
333 }
334 let v = match flag {
335 BINARY_FLAG_INF_POS => INF_POS.clone(),
336 BINARY_FLAG_INF_NEG => INF_NEG.clone(),
337 _ => nan_from_flag(flag)?,
338 };
339 return Ok((v, BINARY_INLINE_LEN));
340 }
341 match flag {
342 BINARY_FLAG_POS | BINARY_FLAG_NEG => decode_inline_finite(bytes, flag),
343 BINARY_FLAG_HEAP_POS | BINARY_FLAG_HEAP_NEG => decode_heap(bytes, flag),
344 _ => Err(Error::InvalidArgument),
345 }
346}
347
348fn decode_inline_finite(bytes: &[u8], flag: u8) -> Result<(ExactNum, usize), Error> {
349 if bytes.len() < BINARY_INLINE_LEN {
350 return Err(Error::InvalidArgument);
351 }
352 let n_sig = bytes[2] as usize;
353 let inexact = bytes[3] != 0;
354 let exp = i32::from_be_bytes(bytes[4..8].try_into().map_err(|_| Error::InvalidArgument)?);
355 let u0 = u32::from_be_bytes(
356 bytes[8..12]
357 .try_into()
358 .map_err(|_| Error::InvalidArgument)?,
359 );
360 let u1 = u32::from_be_bytes(
361 bytes[12..16]
362 .try_into()
363 .map_err(|_| Error::InvalidArgument)?,
364 );
365 let sign = if flag == BINARY_FLAG_NEG { Sign::Neg } else { Sign::Pos };
366 let x = finite_from_u32(&[u0, u1], n_sig, sign, exp, inexact)?;
367 Ok((x, BINARY_INLINE_LEN))
368}
369
370fn decode_heap(bytes: &[u8], flag: u8) -> Result<(ExactNum, usize), Error> {
371 if bytes.len() < BINARY_HEADER_LEN {
372 return Err(Error::InvalidArgument);
373 }
374 let inexact = bytes[3] != 0;
375 let exp = i32::from_be_bytes(bytes[4..8].try_into().map_err(|_| Error::InvalidArgument)?);
376 let n_sig = u32::from_be_bytes(
377 bytes[8..12]
378 .try_into()
379 .map_err(|_| Error::InvalidArgument)?,
380 ) as usize;
381 let n_u32 = u32::from_be_bytes(
382 bytes[12..16]
383 .try_into()
384 .map_err(|_| Error::InvalidArgument)?,
385 ) as usize;
386 if n_u32 == 0 || n_u32 > BINARY_MAX_U32 {
387 return Err(Error::InvalidArgument);
388 }
389 let need = heap_len(n_u32)?;
390 if bytes.len() < need {
391 return Err(Error::InvalidArgument);
392 }
393 let mut u32s = Vec::new();
394 u32s.try_reserve_exact(n_u32)?;
395 let mut off = BINARY_HEADER_LEN;
396 for _ in 0..n_u32 {
397 let chunk: [u8; 4] = bytes[off..off + U32_BYTES]
398 .try_into()
399 .map_err(|_| Error::InvalidArgument)?;
400 u32s.push(u32::from_be_bytes(chunk));
401 off += U32_BYTES;
402 }
403 let sign = if flag == BINARY_FLAG_HEAP_NEG { Sign::Neg } else { Sign::Pos };
404 let x = finite_from_u32(&u32s, n_sig, sign, exp, inexact)?;
405 Ok((x, need))
406}
407
408fn finite_from_u32(
409 u32s: &[u32],
410 n_sig: usize,
411 sign: Sign,
412 exp: Exponent,
413 inexact: bool,
414) -> Result<ExactNum, Error> {
415 if u32s.len() % WORD_U32 != 0 {
416 return Err(Error::InvalidArgument);
417 }
418 if n_sig > u32s.len() * 32 {
419 return Err(Error::InvalidArgument);
420 }
421 let n_words = u32s.len() / WORD_U32;
422 let mut words = Vec::new();
423 words.try_reserve_exact(n_words)?;
424 for i in 0..n_words {
425 words.push(word_from_u32s(&u32s[i * WORD_U32..(i + 1) * WORD_U32]));
426 }
427 let x = ExactNum::from_raw_parts(&words, n_sig, sign, exp, inexact);
428 if x.is_nan() {
429 return Err(x.err().unwrap_or(Error::InvalidArgument));
430 }
431 Ok(x)
432}
433
434fn encoded_array_len(a: &ExactNumArray) -> Result<usize, Error> {
435 if a.len() > BINARY_MAX_ELEMS {
436 return Err(Error::InvalidArgument);
437 }
438 let mut n = BINARY_HEADER_LEN;
439 for v in a.as_slice() {
440 n = n
441 .checked_add(encoded_len(v)?)
442 .ok_or(Error::InvalidArgument)?;
443 }
444 Ok(n)
445}
446
447fn write_array(a: &ExactNumArray, dest: &mut [u8]) -> Result<usize, Error> {
448 let need = encoded_array_len(a)?;
449 if dest.len() < need {
450 return Err(Error::InvalidArgument);
451 }
452 let (rows, cols) = a.shape();
453 dest[..BINARY_HEADER_LEN].fill(0);
454 dest[0] = BINARY_FLAG_ARRAY;
455 dest[1] = BINARY_FORMAT_VERSION;
456 put_u32_be(&mut dest[4..8], u32_from_usize(rows)?);
457 put_u32_be(&mut dest[8..12], u32_from_usize(cols)?);
458 put_u32_be(&mut dest[12..16], u32_from_usize(a.precision())?);
459 let mut off = BINARY_HEADER_LEN;
460 for v in a.as_slice() {
461 off += write_num(v, &mut dest[off..])?;
462 }
463 Ok(need)
464}
465
466fn decode_array(bytes: &[u8]) -> Result<ExactNumArray, Error> {
467 if bytes.len() < BINARY_HEADER_LEN {
468 return Err(Error::InvalidArgument);
469 }
470 if bytes[0] != BINARY_FLAG_ARRAY || bytes[1] != BINARY_FORMAT_VERSION {
471 return Err(Error::InvalidArgument);
472 }
473 let rows =
474 u32::from_be_bytes(bytes[4..8].try_into().map_err(|_| Error::InvalidArgument)?) as usize;
475 let cols = u32::from_be_bytes(
476 bytes[8..12]
477 .try_into()
478 .map_err(|_| Error::InvalidArgument)?,
479 ) as usize;
480 let p = u32::from_be_bytes(
481 bytes[12..16]
482 .try_into()
483 .map_err(|_| Error::InvalidArgument)?,
484 ) as usize;
485 let n = rows.checked_mul(cols).ok_or(Error::InvalidArgument)?;
486 if n > BINARY_MAX_ELEMS {
487 return Err(Error::InvalidArgument);
488 }
489 let mut vals = Vec::new();
490 vals.try_reserve_exact(n)?;
491 let mut off = BINARY_HEADER_LEN;
492 for _ in 0..n {
493 let (v, used) = decode_num(&bytes[off..])?;
494 off += used;
495 vals.push(v);
496 }
497 if off != bytes.len() {
498 return Err(Error::InvalidArgument);
499 }
500 ExactNumArray::from_parts(p, rows, cols, vals)
501}
502
503fn u32_count(n_words: usize) -> usize {
504 n_words * WORD_U32
505}
506
507fn words_to_u32(words: &[Word], out: &mut [u32]) -> Result<(), Error> {
508 let need = u32_count(words.len());
509 if out.len() < need {
510 return Err(Error::InvalidArgument);
511 }
512 let mut i = 0;
513 for &w in words {
514 #[cfg(target_pointer_width = "32")]
515 {
516 out[i] = w;
517 i += 1;
518 }
519 #[cfg(not(target_pointer_width = "32"))]
520 {
521 out[i] = w as u32;
522 out[i + 1] = (w >> 32) as u32;
523 i += 2;
524 }
525 }
526 let _ = i;
527 Ok(())
528}
529
530fn word_from_u32s(part: &[u32]) -> Word {
531 #[cfg(target_pointer_width = "32")]
532 {
533 part[0]
534 }
535 #[cfg(not(target_pointer_width = "32"))]
536 {
537 (part[0] as Word) | ((part[1] as Word) << 32)
538 }
539}
540
541fn put_u32_be(dest: &mut [u8], v: u32) {
542 dest[..U32_BYTES].copy_from_slice(&v.to_be_bytes());
543}
544
545fn u32_from_usize(v: usize) -> Result<u32, Error> {
546 u32::try_from(v).map_err(|_| Error::InvalidArgument)
547}
548
549#[cfg(test)]
550mod tests {
551 use super::*;
552 use crate::RoundingMode;
553
554 fn p64() -> usize {
555 64
556 }
557
558 fn raw_eq(a: &ExactNum, b: &ExactNum) -> bool {
559 a.as_raw_parts() == b.as_raw_parts()
560 }
561
562 #[test]
563 fn binary_inline_roundtrip_finite() {
564 let p = p64();
565 let vals = [
566 ExactNum::from_u8(0, p),
567 ExactNum::from_u8(1, p),
568 ExactNum::from_u8(1, p).neg(),
569 ExactNum::from_u32(u32::MAX, p),
570 ExactNum::from_u8(1, p).div(&ExactNum::from_u8(3, p), p, RoundingMode::ToEven),
571 ];
572 for x in &vals {
573 let buf = x.to_inline_bytes().unwrap();
574 assert_eq!(buf.as_bytes().len(), BINARY_INLINE_LEN);
575 assert_eq!(buf.as_bytes()[1], BINARY_FORMAT_VERSION);
576 let y = ExactNum::from_inline_bytes(buf.as_bytes()).unwrap();
577 assert!(raw_eq(x, &y), "inline limbs");
578 assert_eq!(x.cmp(&y), Some(0));
579 let v = x.to_bytes().unwrap();
580 assert_eq!(v.len(), BINARY_INLINE_LEN);
581 let z = ExactNum::from_bytes(&v).unwrap();
582 assert!(raw_eq(x, &z));
583 }
584 }
585
586 #[test]
587 fn binary_nan_flag_is_recognizable() {
588 let buf = NAN.to_inline_bytes().unwrap();
589 assert_eq!(buf.as_bytes()[0], BINARY_FLAG_NAN_BARE);
590 assert_eq!(buf.as_bytes()[1], BINARY_FORMAT_VERSION);
591 let y = ExactNum::from_inline_bytes(buf.as_bytes()).unwrap();
592 assert!(y.is_nan());
593 let div = ExactNum::nan(Some(Error::DivisionByZero));
594 let d = div.to_bytes().unwrap();
595 assert_eq!(d[0], BINARY_FLAG_NAN_DIV0);
596 let back = ExactNum::from_bytes(&d).unwrap();
597 assert!(back.is_nan());
598 assert_eq!(back.err(), Some(Error::DivisionByZero));
599 }
600
601 #[test]
602 fn binary_inf_roundtrip() {
603 let p = INF_POS.to_bytes().unwrap();
604 let n = INF_NEG.to_bytes().unwrap();
605 assert_eq!(p[0], BINARY_FLAG_INF_POS);
606 assert_eq!(n[0], BINARY_FLAG_INF_NEG);
607 assert!(ExactNum::from_bytes(&p).unwrap().is_inf_pos());
608 assert!(ExactNum::from_bytes(&n).unwrap().is_inf_neg());
609 }
610
611 #[test]
612 fn binary_heap_roundtrip_p256() {
613 let p = 256;
614 let x = ExactNum::from_u8(1, p).div(&ExactNum::from_u8(3, p), p, RoundingMode::ToEven);
615 assert!(x.mantissa_max_bit_len().unwrap() > BINARY_INLINE_MANT_BITS);
616 assert!(x.to_inline_bytes().is_err());
617 let v = x.to_bytes().unwrap();
618 assert!(v.len() > BINARY_INLINE_LEN);
619 assert_eq!(v[0], BINARY_FLAG_HEAP_POS);
620 let y = ExactNum::from_bytes(&v).unwrap();
621 assert!(raw_eq(&x, &y));
622 assert_eq!(x.cmp(&y), Some(0));
623 }
624
625 #[test]
626 fn binary_array_roundtrip_and_shape_err() {
627 let p = p64();
628 let n = |k: u8| ExactNum::from_u8(k, p);
629 let a = ExactNumArray::from_shape(p, 2, 2, &[n(1), n(2), n(3), n(4)]).unwrap();
630 let v = a.to_bytes().unwrap();
631 assert_eq!(v[0], BINARY_FLAG_ARRAY);
632 let b = ExactNumArray::from_bytes(&v).unwrap();
633 assert_eq!(b.shape(), (2, 2));
634 assert_eq!(b.precision(), p);
635 for i in 0..4 {
636 assert_eq!(a.get(i).unwrap().cmp(b.get(i).unwrap()), Some(0));
637 assert!(raw_eq(a.get(i).unwrap(), b.get(i).unwrap()));
638 }
639 let mut bad = v.clone();
640 bad[4..8].copy_from_slice(&3u32.to_be_bytes());
642 assert!(ExactNumArray::from_bytes(&bad).is_err());
643 }
644
645 #[test]
646 fn binary_invalid_bytes_are_err() {
647 assert!(ExactNum::from_bytes(&[]).is_err());
648 assert!(ExactNum::from_bytes(&[0]).is_err());
649 assert!(ExactNum::from_bytes(&[BINARY_FLAG_POS, 0]).is_err());
650 assert!(ExactNum::from_bytes(&[0x7F, BINARY_FORMAT_VERSION]).is_err());
651 let mut short = NAN.to_bytes().unwrap();
652 short.pop();
653 assert!(ExactNum::from_bytes(&short).is_err());
654 let mut extra = ExactNum::from_u8(1, p64()).to_bytes().unwrap();
655 extra.push(0);
656 assert!(ExactNum::from_bytes(&extra).is_err());
657 assert!(ExactNumArray::from_bytes(&[BINARY_FLAG_ARRAY, BINARY_FORMAT_VERSION]).is_err());
658 }
659
660 #[test]
661 fn binary_write_bytes_zero_alloc_inline() {
662 let x = ExactNum::from_u8(2, p64());
663 let mut dest = [0u8; BINARY_INLINE_LEN];
664 let n = x.write_inline_bytes(&mut dest).unwrap();
665 assert_eq!(n, BINARY_INLINE_LEN);
666 assert_eq!(dest, x.to_inline_bytes().unwrap().into_bytes());
667 assert!(x.write_inline_bytes(&mut dest[..8]).is_err());
668 }
669
670 #[test]
671 fn u32_word_boundary_add_is_2_pow_32() {
672 let p = p64();
673 let a = ExactNum::from_u32(u32::MAX, p);
674 let b = ExactNum::from_u8(1, p);
675 let s = a.add(&b, p, RoundingMode::ToEven);
676 let expect = ExactNum::from_u64(1u64 << 32, p);
677 assert_eq!(s.cmp(&expect), Some(0));
678 assert!(s.err().is_none());
679 let buf = s.to_inline_bytes().unwrap();
680 assert!(raw_eq(
681 &s,
682 &ExactNum::from_inline_bytes(buf.as_bytes()).unwrap()
683 ));
684 }
685}