1#[cfg(feature = "mem_dbg")]
10use mem_dbg::{MemDbg, MemSize};
11
12use crate::prelude::{
13 Codes, bit_len_vbyte, len_delta, len_exp_golomb, len_gamma, len_minimal_binary, len_omega,
14 len_pi, len_zeta,
15};
16use core::fmt::Debug;
17
18#[cfg(feature = "std")]
19use crate::dispatch::{CodesRead, CodesWrite};
20#[cfg(feature = "std")]
21use crate::prelude::Endianness;
22#[cfg(feature = "std")]
23use crate::prelude::{DynamicCodeRead, DynamicCodeWrite, StaticCodeRead, StaticCodeWrite};
24#[cfg(feature = "std")]
25use std::sync::Mutex;
26
27#[cfg(feature = "serde")]
28use alloc::string::ToString;
29#[cfg(feature = "alloc")]
30use alloc::{vec, vec::Vec};
31
32#[derive(Debug, Copy, Clone, PartialEq, Eq)]
47#[cfg_attr(feature = "mem_dbg", derive(MemDbg, MemSize))]
48pub struct CodesStats<
49 const ZETA: usize = 10,
51 const GOLOMB: usize = 10,
53 const EXP_GOLOMB: usize = 10,
55 const RICE: usize = 10,
57 const PI: usize = 10,
59> {
60 pub total: u64,
62 pub unary: u64,
65 pub gamma: u64,
68 pub delta: u64,
71 pub omega: u64,
74 pub vbyte: u64,
77 pub zeta: [u64; ZETA],
80 pub golomb: [u64; GOLOMB],
84 pub exp_golomb: [u64; EXP_GOLOMB],
89 pub rice: [u64; RICE],
93 pub pi: [u64; PI],
96}
97
98impl<
99 const ZETA: usize,
100 const GOLOMB: usize,
101 const EXP_GOLOMB: usize,
102 const RICE: usize,
103 const PI: usize,
104> core::default::Default for CodesStats<ZETA, GOLOMB, EXP_GOLOMB, RICE, PI>
105{
106 fn default() -> Self {
107 Self {
108 total: 0,
109 unary: 0,
110 gamma: 0,
111 delta: 0,
112 omega: 0,
113 vbyte: 0,
114 zeta: [0; ZETA],
115 golomb: [0; GOLOMB],
116 exp_golomb: [0; EXP_GOLOMB],
117 rice: [0; RICE],
118 pi: [0; PI],
119 }
120 }
121}
122
123impl<
124 const ZETA: usize,
125 const GOLOMB: usize,
126 const EXP_GOLOMB: usize,
127 const RICE: usize,
128 const PI: usize,
129> CodesStats<ZETA, GOLOMB, EXP_GOLOMB, RICE, PI>
130{
131 pub fn update(&mut self, n: u64) -> u64 {
134 self.update_many(n, 1)
135 }
136
137 #[inline]
147 pub fn update_many(&mut self, n: u64, count: u64) -> u64 {
148 if count == 0 {
150 return n;
151 }
152 self.total = self.total.saturating_add(count);
153
154 self.unary = self.unary.saturating_add((n + 1).saturating_mul(count));
159 self.gamma = self
160 .gamma
161 .saturating_add((len_gamma(n) as u64).saturating_mul(count));
162 self.delta = self
163 .delta
164 .saturating_add((len_delta(n) as u64).saturating_mul(count));
165 self.omega = self
166 .omega
167 .saturating_add((len_omega(n) as u64).saturating_mul(count));
168 self.vbyte = self
169 .vbyte
170 .saturating_add((bit_len_vbyte(n) as u64).saturating_mul(count));
171 for (k, val) in self.zeta.iter_mut().enumerate() {
172 *val = val.saturating_add((len_zeta(n, (k + 1) as _) as u64).saturating_mul(count));
173 }
174 for (b, val) in self.golomb.iter_mut().enumerate() {
175 let b = (b + 1) as u64;
178 let len = (n / b) + 1 + len_minimal_binary(n % b, b) as u64;
179 *val = val.saturating_add(len.saturating_mul(count));
180 }
181 for (k, val) in self.exp_golomb.iter_mut().enumerate() {
182 *val = val.saturating_add((len_exp_golomb(n, k as _) as u64).saturating_mul(count));
183 }
184 for (log2_b, val) in self.rice.iter_mut().enumerate() {
185 let len = (n >> log2_b) + 1 + log2_b as u64;
187 *val = val.saturating_add(len.saturating_mul(count));
188 }
189 for (k, val) in self.pi.iter_mut().enumerate() {
190 *val = val.saturating_add((len_pi(n, (k + 2) as _) as u64).saturating_mul(count));
191 }
192 n
193 }
194
195 pub fn add(&mut self, rhs: &Self) {
197 self.total = self.total.saturating_add(rhs.total);
198 self.unary = self.unary.saturating_add(rhs.unary);
199 self.gamma = self.gamma.saturating_add(rhs.gamma);
200 self.delta = self.delta.saturating_add(rhs.delta);
201 self.omega = self.omega.saturating_add(rhs.omega);
202 self.vbyte = self.vbyte.saturating_add(rhs.vbyte);
203 for (a, b) in self.zeta.iter_mut().zip(rhs.zeta.iter()) {
204 *a = a.saturating_add(*b);
205 }
206 for (a, b) in self.golomb.iter_mut().zip(rhs.golomb.iter()) {
207 *a = a.saturating_add(*b);
208 }
209 for (a, b) in self.exp_golomb.iter_mut().zip(rhs.exp_golomb.iter()) {
210 *a = a.saturating_add(*b);
211 }
212 for (a, b) in self.rice.iter_mut().zip(rhs.rice.iter()) {
213 *a = a.saturating_add(*b);
214 }
215 for (a, b) in self.pi.iter_mut().zip(rhs.pi.iter()) {
216 *a = a.saturating_add(*b);
217 }
218 }
219
220 #[must_use]
225 pub fn best_code(&self) -> (Codes, u64) {
226 let mut best = (Codes::Unary, self.unary);
227 if self.gamma < best.1 {
228 best = (Codes::Gamma, self.gamma);
229 }
230 if self.delta < best.1 {
231 best = (Codes::Delta, self.delta);
232 }
233 if self.omega < best.1 {
234 best = (Codes::Omega, self.omega);
235 }
236 if self.vbyte < best.1 {
237 best = (Codes::VByteBe, self.vbyte);
238 }
239 for (k, val) in self.zeta.iter().enumerate() {
240 if *val < best.1 {
241 best = (Codes::Zeta((k + 1) as _), *val);
242 }
243 }
244 for (b, val) in self.golomb.iter().enumerate() {
245 if *val < best.1 {
246 best = (Codes::Golomb((b + 1) as _), *val);
247 }
248 }
249 for (k, val) in self.exp_golomb.iter().enumerate() {
250 if *val < best.1 {
251 best = (Codes::ExpGolomb(k as _), *val);
252 }
253 }
254 for (log2_b, val) in self.rice.iter().enumerate() {
255 if *val < best.1 {
256 best = (Codes::Rice(log2_b as _), *val);
257 }
258 }
259 for (k, val) in self.pi.iter().enumerate() {
260 if *val < best.1 {
261 best = (Codes::Pi((k + 2) as _), *val);
262 }
263 }
264 best
265 }
266
267 #[cfg(feature = "alloc")]
269 #[must_use]
270 pub fn get_codes(&self) -> Vec<(Codes, u64)> {
271 let mut codes = vec![
272 (Codes::Unary, self.unary),
273 (Codes::Gamma, self.gamma),
274 (Codes::Delta, self.delta),
275 (Codes::Omega, self.omega),
276 (Codes::VByteBe, self.vbyte),
277 ];
278 for (k, val) in self.zeta.iter().enumerate() {
279 codes.push((Codes::Zeta((k + 1) as _), *val));
280 }
281 for (b, val) in self.golomb.iter().enumerate() {
282 codes.push((Codes::Golomb((b + 1) as _), *val));
283 }
284 for (k, val) in self.exp_golomb.iter().enumerate() {
285 codes.push((Codes::ExpGolomb(k as _), *val));
286 }
287 for (log2_b, val) in self.rice.iter().enumerate() {
288 codes.push((Codes::Rice(log2_b as _), *val));
289 }
290 for (k, val) in self.pi.iter().enumerate() {
291 codes.push((Codes::Pi((k + 2) as _), *val));
292 }
293 codes.sort_by_key(|&(_, len)| len);
295 codes
296 }
297
298 #[must_use]
300 pub fn bits_for(&self, code: Codes) -> Option<u64> {
301 match code {
302 Codes::Unary => Some(self.unary),
303 Codes::Gamma => Some(self.gamma),
304 Codes::Delta => Some(self.delta),
305 Codes::Omega => Some(self.omega),
306 Codes::VByteBe | Codes::VByteLe => Some(self.vbyte),
307 Codes::Zeta(k) => self.zeta.get(k.checked_sub(1)?).copied(),
308 Codes::Golomb(b) => self.golomb.get(b.checked_sub(1)? as usize).copied(),
309 Codes::ExpGolomb(k) => self.exp_golomb.get(k).copied(),
310 Codes::Rice(log2_b) => self.rice.get(log2_b).copied(),
311 Codes::Pi(k) => self.pi.get(k.checked_sub(2)?).copied(),
312 }
313 }
314}
315
316impl<
317 const ZETA: usize,
318 const GOLOMB: usize,
319 const EXP_GOLOMB: usize,
320 const RICE: usize,
321 const PI: usize,
322> core::ops::AddAssign for CodesStats<ZETA, GOLOMB, EXP_GOLOMB, RICE, PI>
323{
324 fn add_assign(&mut self, rhs: Self) {
326 self.add(&rhs);
327 }
328}
329
330impl<
331 const ZETA: usize,
332 const GOLOMB: usize,
333 const EXP_GOLOMB: usize,
334 const RICE: usize,
335 const PI: usize,
336> core::ops::Add for CodesStats<ZETA, GOLOMB, EXP_GOLOMB, RICE, PI>
337{
338 type Output = Self;
339
340 fn add(self, rhs: Self) -> Self {
342 let mut res = self;
343 res += rhs;
344 res
345 }
346}
347
348impl<
350 const ZETA: usize,
351 const GOLOMB: usize,
352 const EXP_GOLOMB: usize,
353 const RICE: usize,
354 const PI: usize,
355> core::iter::Sum for CodesStats<ZETA, GOLOMB, EXP_GOLOMB, RICE, PI>
356{
357 fn sum<I: Iterator<Item = Self>>(iter: I) -> Self {
358 iter.fold(Self::default(), |a, b| a + b)
359 }
360}
361
362#[cfg(feature = "serde")]
363impl<
364 const ZETA: usize,
365 const GOLOMB: usize,
366 const EXP_GOLOMB: usize,
367 const RICE: usize,
368 const PI: usize,
369> serde::Serialize for CodesStats<ZETA, GOLOMB, EXP_GOLOMB, RICE, PI>
370{
371 fn serialize<S: serde::Serializer>(&self, serializer: S) -> Result<S::Ok, S::Error> {
372 use serde::ser::SerializeStruct;
373
374 let mut state = serializer.serialize_struct("CodesStats", 11)?;
375 state.serialize_field("total", &self.total)?;
376 state.serialize_field("unary", &self.unary)?;
377 state.serialize_field("gamma", &self.gamma)?;
378 state.serialize_field("delta", &self.delta)?;
379 state.serialize_field("omega", &self.omega)?;
380 state.serialize_field("vbyte", &self.vbyte)?;
381 state.serialize_field("zeta", &self.zeta.as_slice())?;
383 state.serialize_field("golomb", &self.golomb.as_slice())?;
384 state.serialize_field("exp_golomb", &self.exp_golomb.as_slice())?;
385 state.serialize_field("rice", &self.rice.as_slice())?;
386 state.serialize_field("pi", &self.pi.as_slice())?;
387 state.end()
388 }
389}
390
391#[cfg(feature = "serde")]
392impl<
393 'de,
394 const ZETA: usize,
395 const GOLOMB: usize,
396 const EXP_GOLOMB: usize,
397 const RICE: usize,
398 const PI: usize,
399> serde::Deserialize<'de> for CodesStats<ZETA, GOLOMB, EXP_GOLOMB, RICE, PI>
400{
401 fn deserialize<D: serde::Deserializer<'de>>(deserializer: D) -> Result<Self, D::Error> {
402 use serde::de::{MapAccess, Visitor};
403
404 struct CodesStatsVisitor<
405 const ZETA: usize,
406 const GOLOMB: usize,
407 const EXP_GOLOMB: usize,
408 const RICE: usize,
409 const PI: usize,
410 >;
411
412 impl<
413 'de,
414 const ZETA: usize,
415 const GOLOMB: usize,
416 const EXP_GOLOMB: usize,
417 const RICE: usize,
418 const PI: usize,
419 > Visitor<'de> for CodesStatsVisitor<ZETA, GOLOMB, EXP_GOLOMB, RICE, PI>
420 {
421 type Value = CodesStats<ZETA, GOLOMB, EXP_GOLOMB, RICE, PI>;
422
423 fn expecting(&self, formatter: &mut core::fmt::Formatter) -> core::fmt::Result {
424 formatter.write_str("struct CodesStats")
425 }
426
427 fn visit_map<V: MapAccess<'de>>(self, mut map: V) -> Result<Self::Value, V::Error> {
428 let mut total = None;
429 let mut unary = None;
430 let mut gamma = None;
431 let mut delta = None;
432 let mut omega = None;
433 let mut vbyte = None;
434 let mut zeta: Option<[u64; ZETA]> = None;
435 let mut golomb: Option<[u64; GOLOMB]> = None;
436 let mut exp_golomb: Option<[u64; EXP_GOLOMB]> = None;
437 let mut rice: Option<[u64; RICE]> = None;
438 let mut pi: Option<[u64; PI]> = None;
439
440 fn vec_to_array<E: serde::de::Error, const N: usize>(
442 v: Vec<u64>,
443 ) -> Result<[u64; N], E> {
444 v.try_into().map_err(|v: Vec<u64>| {
445 serde::de::Error::invalid_length(v.len(), &N.to_string().as_str())
446 })
447 }
448
449 while let Some(key) = map.next_key::<&str>()? {
450 match key {
451 "total" => total = Some(map.next_value()?),
452 "unary" => unary = Some(map.next_value()?),
453 "gamma" => gamma = Some(map.next_value()?),
454 "delta" => delta = Some(map.next_value()?),
455 "omega" => omega = Some(map.next_value()?),
456 "vbyte" => vbyte = Some(map.next_value()?),
457 "zeta" => zeta = Some(vec_to_array(map.next_value()?)?),
458 "golomb" => golomb = Some(vec_to_array(map.next_value()?)?),
459 "exp_golomb" => exp_golomb = Some(vec_to_array(map.next_value()?)?),
460 "rice" => rice = Some(vec_to_array(map.next_value()?)?),
461 "pi" => pi = Some(vec_to_array(map.next_value()?)?),
462 _ => {
463 let _ = map.next_value::<serde::de::IgnoredAny>()?;
464 }
465 }
466 }
467
468 Ok(CodesStats {
469 total: total.unwrap_or_default(),
470 unary: unary.unwrap_or_default(),
471 gamma: gamma.unwrap_or_default(),
472 delta: delta.unwrap_or_default(),
473 omega: omega.unwrap_or_default(),
474 vbyte: vbyte.unwrap_or_default(),
475 zeta: zeta.unwrap_or([0; ZETA]),
476 golomb: golomb.unwrap_or([0; GOLOMB]),
477 exp_golomb: exp_golomb.unwrap_or([0; EXP_GOLOMB]),
478 rice: rice.unwrap_or([0; RICE]),
479 pi: pi.unwrap_or([0; PI]),
480 })
481 }
482 }
483
484 deserializer.deserialize_struct(
485 "CodesStats",
486 &[
487 "total",
488 "unary",
489 "gamma",
490 "delta",
491 "omega",
492 "vbyte",
493 "zeta",
494 "golomb",
495 "exp_golomb",
496 "rice",
497 "pi",
498 ],
499 CodesStatsVisitor::<ZETA, GOLOMB, EXP_GOLOMB, RICE, PI>,
500 )
501 }
502}
503
504#[derive(Debug)]
508#[cfg_attr(feature = "mem_dbg", derive(MemDbg, MemSize))]
509#[cfg(feature = "std")]
510pub struct CodesStatsWrapper<
511 W,
512 const ZETA: usize = 10,
514 const GOLOMB: usize = 10,
516 const EXP_GOLOMB: usize = 10,
518 const RICE: usize = 10,
520 const PI: usize = 10,
522> {
523 stats: Mutex<CodesStats<ZETA, GOLOMB, EXP_GOLOMB, RICE, PI>>,
527 wrapped: W,
528}
529
530#[cfg(feature = "std")]
531impl<
532 W,
533 const ZETA: usize,
534 const GOLOMB: usize,
535 const EXP_GOLOMB: usize,
536 const RICE: usize,
537 const PI: usize,
538> CodesStatsWrapper<W, ZETA, GOLOMB, EXP_GOLOMB, RICE, PI>
539{
540 #[must_use]
542 pub fn new(wrapped: W) -> Self {
543 Self {
544 stats: Mutex::new(CodesStats::default()),
545 wrapped,
546 }
547 }
548
549 pub const fn stats(&self) -> &Mutex<CodesStats<ZETA, GOLOMB, EXP_GOLOMB, RICE, PI>> {
551 &self.stats
552 }
553
554 #[must_use]
556 pub fn into_inner(self) -> (W, CodesStats<ZETA, GOLOMB, EXP_GOLOMB, RICE, PI>) {
557 (
558 self.wrapped,
559 self.stats.into_inner().expect("mutex is not poisoned"),
560 )
561 }
562}
563
564#[cfg(feature = "std")]
565impl<
566 W: DynamicCodeRead,
567 const ZETA: usize,
568 const GOLOMB: usize,
569 const EXP_GOLOMB: usize,
570 const RICE: usize,
571 const PI: usize,
572> DynamicCodeRead for CodesStatsWrapper<W, ZETA, GOLOMB, EXP_GOLOMB, RICE, PI>
573{
574 #[inline]
575 fn read<E: Endianness, CR: CodesRead<E> + ?Sized>(
576 &self,
577 reader: &mut CR,
578 ) -> Result<u64, CR::Error> {
579 let res = self.wrapped.read(reader)?;
580 self.stats.lock().unwrap().update(res);
581 Ok(res)
582 }
583}
584
585#[cfg(feature = "std")]
586impl<
587 W: StaticCodeRead<E, CR>,
588 const ZETA: usize,
589 const GOLOMB: usize,
590 const EXP_GOLOMB: usize,
591 const RICE: usize,
592 const PI: usize,
593 E: Endianness,
594 CR: CodesRead<E> + ?Sized,
595> StaticCodeRead<E, CR> for CodesStatsWrapper<W, ZETA, GOLOMB, EXP_GOLOMB, RICE, PI>
596{
597 #[inline]
598 fn read(&self, reader: &mut CR) -> Result<u64, CR::Error> {
599 let res = self.wrapped.read(reader)?;
600 self.stats.lock().unwrap().update(res);
601 Ok(res)
602 }
603}
604
605#[cfg(feature = "std")]
606impl<
607 W: DynamicCodeWrite,
608 const ZETA: usize,
609 const GOLOMB: usize,
610 const EXP_GOLOMB: usize,
611 const RICE: usize,
612 const PI: usize,
613> DynamicCodeWrite for CodesStatsWrapper<W, ZETA, GOLOMB, EXP_GOLOMB, RICE, PI>
614{
615 #[inline]
616 fn write<E: Endianness, CW: CodesWrite<E> + ?Sized>(
617 &self,
618 writer: &mut CW,
619 n: u64,
620 ) -> Result<usize, CW::Error> {
621 let res = self.wrapped.write(writer, n)?;
622 self.stats.lock().unwrap().update(n);
623 Ok(res)
624 }
625}
626
627#[cfg(feature = "std")]
628impl<
629 W: StaticCodeWrite<E, CW>,
630 const ZETA: usize,
631 const GOLOMB: usize,
632 const EXP_GOLOMB: usize,
633 const RICE: usize,
634 const PI: usize,
635 E: Endianness,
636 CW: CodesWrite<E> + ?Sized,
637> StaticCodeWrite<E, CW> for CodesStatsWrapper<W, ZETA, GOLOMB, EXP_GOLOMB, RICE, PI>
638{
639 #[inline]
640 fn write(&self, writer: &mut CW, n: u64) -> Result<usize, CW::Error> {
641 let res = self.wrapped.write(writer, n)?;
642 self.stats.lock().unwrap().update(n);
643 Ok(res)
644 }
645}
646
647#[cfg(test)]
648#[cfg(feature = "serde")]
649mod serde_tests {
650 use super::*;
651
652 #[test]
653 fn test_serde_code_stats() -> serde_json::Result<()> {
654 let mut stats: CodesStats = CodesStats::default();
655 for i in 0..100 {
656 stats.update(i);
657 }
658 let json = serde_json::to_string(&stats)?;
659 let deserialized: CodesStats = serde_json::from_str(&json)?;
660 assert_eq!(stats, deserialized);
661 Ok(())
662 }
663
664 #[test]
665 fn test_roundtrip_different_sizes() -> serde_json::Result<()> {
666 let mut stats: CodesStats<10, 20, 5, 8, 6> = CodesStats::default();
667 for i in 0..1000 {
668 stats.update(i);
669 }
670 let json = serde_json::to_string_pretty(&stats)?;
671 let deserialized: CodesStats<10, 20, 5, 8, 6> = serde_json::from_str(&json)?;
672 assert_eq!(stats, deserialized);
673 Ok(())
674 }
675
676 #[test]
677 #[should_panic]
678 fn test_mismatched_sizes() {
679 let mut stats: CodesStats<10, 20, 5, 8, 6> = CodesStats::default();
680 for i in 0..1000 {
681 stats.update(i);
682 }
683 let json = serde_json::to_string_pretty(&stats).unwrap();
684 let _deserialized: CodesStats<10, 21, 5, 8, 6> = serde_json::from_str(&json).unwrap();
686 }
687}
688
689#[cfg(test)]
690mod robustness_tests {
691 use super::*;
692
693 #[test]
694 #[should_panic]
695 fn u64_max_panics_as_documented() {
696 let mut stats: CodesStats = CodesStats::default();
697 stats.update(u64::MAX);
701 }
702
703 #[test]
704 fn test_count_zero_is_a_noop() {
705 let mut stats: CodesStats = CodesStats::default();
706 let before = stats;
707 stats.update_many(u64::MAX, 0);
708 assert_eq!(
709 stats, before,
710 "update_many with count 0 must change nothing"
711 );
712 }
713
714 #[test]
715 fn test_large_count_saturates_without_panic() {
716 let mut stats: CodesStats = CodesStats::default();
717 stats.update_many(1 << 40, 1 << 30);
720 let (_, bits) = stats.best_code();
721 assert!(bits < u64::MAX);
723 }
724}