//! Radix-generic, lexical integer-to-string conversion routines.
//!
//! These routines are optimized for code size: they are significantly
//! slower at the cost of smaller binary size.

#![cfg(feature = "compact")]
#![doc(hidden)]

use lexical_util::algorithm::copy_to_dst;
use lexical_util::constants::FormattedSize;
use lexical_util::digit::digit_to_char;
use lexical_util::num::{AsCast, UnsignedInteger};

// NOTE: Testing our algorithms can be done effectively as:
//  table = [
//      '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'A', 'B', 'C', 'D',
//      'E', 'F', 'G', 'H', 'I', 'J', 'K', 'L', 'M', 'N', 'O', 'P', 'Q', 'R',
//      'S', 'T', 'U', 'V', 'W', 'X', 'Y', 'Z',
//  ]
//  def to_string(value, radix):
//      result = ''
//      while value >= radix:
//          r = value % radix
//          value //= radix
//          result = table[int(r)] + result
//
//      r = value % radix
//      result = table[int(r)] + result
//
//      return result

/// Write integer to string.
pub trait Compact: UnsignedInteger + FormattedSize {
    /// Write our integer to string without optimizations.
    ///
    /// This iterates over the buffer in reverse, and ensures that at least
    /// 128 elements exist in the temporary buffer. This ensures that the digits
    /// can never overflow, even in base 2. The buffer then must be able to hold
    /// at least 128 digits as well, after subslicing the data. This guarantee
    /// is likely already made.
    fn compact(self, radix: u32, buffer: &mut [u8]) -> usize {
        // NOTE: We do not have to validate the buffer length because `copy_to_dst` is
        // safe.
        assert!(Self::BITS <= 128);
        let mut digits: [u8; 128] = [0u8; 128];
        let mut index = digits.len();

        // Decode all but the last digit.
        let radix = Self::from_u32(radix);
        let mut value = self;
        while value >= radix {
            let r = value % radix;
            value /= radix;
            index -= 1;
            digits[index] = digit_to_char(u32::as_cast(r));
        }

        // Decode last digit.
        let r = value;
        index -= 1;
        digits[index] = digit_to_char(u32::as_cast(r));
        let slc = &digits[index..];
        copy_to_dst(buffer, slc)
    }
}

macro_rules! compact_impl {
    ($($t:ty)*) => ($(
        impl Compact for $t {}
    )*)
}

compact_impl! { u8 u16 u32 u64 u128 usize }
