Last active
August 29, 2023 21:25
-
-
Save AnthonyMikh/50b4e384cf217e192b0b9af144684e26 to your computer and use it in GitHub Desktop.
Как написать Fizzbuzz в .rodata
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
[package] | |
name = "fizzbuzz" | |
version = "0.1.0" | |
edition = "2021" | |
[features] | |
use_nightly = [] | |
compare_with_previous_impl = [] |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
#![cfg_attr(feature = "use_nightly", feature(generic_const_exprs), allow(incomplete_features))] | |
struct Z; | |
struct S<T>(T); | |
trait Add<Rhs> { | |
type Sum; | |
} | |
type SumOf<N, M> = <N as Add<M>>::Sum; | |
impl<N> Add<N> for Z { | |
type Sum = N; | |
} | |
impl<N, M> Add<M> for S<N> | |
where | |
N: Add<S<M>>, | |
{ | |
type Sum = SumOf<N, S<M>>; | |
} | |
#[cfg(not(feature = "use_nightly"))] | |
type One = S<Z>; | |
#[cfg(not(feature = "use_nightly"))] | |
type Two = SumOf<One, One>; | |
#[cfg(not(feature = "use_nightly"))] | |
type Three = SumOf<One, Two>; | |
#[cfg(feature = "use_nightly")] | |
type Three = S<S<S<Z>>>; | |
#[cfg(not(feature = "use_nightly"))] | |
type Five = SumOf<Three, Two>; | |
#[cfg(feature = "use_nightly")] | |
type Five = S<S<S<S<S<Z>>>>>; | |
type Ten = SumOf<Five, Five>; | |
type TwentyFive = SumOf<Five, SumOf<Ten, Ten>>; | |
type Fifty = SumOf<TwentyFive, TwentyFive>; | |
type OneHundred = SumOf<Fifty, Fifty>; | |
type N = OneHundred; | |
struct Nil; | |
struct Cons<H, T>(H, T); | |
trait RangeDownFrom { | |
type Output; | |
} | |
impl RangeDownFrom for Z { | |
type Output = Cons<Z, Nil>; | |
} | |
impl<N> RangeDownFrom for S<N> | |
where | |
N: RangeDownFrom, | |
{ | |
type Output = Cons<S<N>, N::Output>; | |
} | |
type MakeRangeDownFrom<N> = <N as RangeDownFrom>::Output; | |
trait ReverseWith<Acc> { | |
type Output; | |
} | |
impl<Acc> ReverseWith<Acc> for Nil { | |
type Output = Acc; | |
} | |
impl<Head, Tail, Acc> ReverseWith<Acc> for Cons<Head, Tail> | |
where | |
Tail: ReverseWith<Cons<Head, Acc>>, | |
{ | |
type Output = <Tail as ReverseWith<Cons<Head, Acc>>>::Output; | |
} | |
type Reverse<List> = <List as ReverseWith<Nil>>::Output; | |
type RangeTo<N> = Reverse<MakeRangeDownFrom<N>>; | |
trait DecrementByMod<M> { | |
type Output; | |
} | |
impl<T> DecrementByMod<S<T>> for Z { | |
type Output = T; | |
} | |
impl<M, T> DecrementByMod<M> for S<T> { | |
type Output = T; | |
} | |
type DecMod<T, M> = <T as DecrementByMod<M>>::Output; | |
trait EnumerateFromWithCycle<Start, N> { | |
type Output; | |
} | |
type EnumeratedFromWithCycle<T, Start, N> = <T as EnumerateFromWithCycle<Start, N>>::Output; | |
impl<Start, N> EnumerateFromWithCycle<Start, N> for Nil { | |
type Output = Nil; | |
} | |
impl<Start, N, Head, Tail> EnumerateFromWithCycle<Start, N> for Cons<Head, Tail> | |
where | |
Start: DecrementByMod<N>, | |
Tail: EnumerateFromWithCycle<DecMod<Start, N>, N>, | |
{ | |
type Output = Cons<(Head, Start), EnumeratedFromWithCycle<Tail, DecMod<Start, N>, N>>; | |
} | |
type EnumerateFromZeroWithCycle<T, N> = <T as EnumerateFromWithCycle<Z, N>>::Output; | |
type EnumerateFromZeroWithCycle3<T> = EnumerateFromZeroWithCycle<T, Three>; | |
type EnumerateFromZeroWithCycle5<T> = EnumerateFromZeroWithCycle<T, Five>; | |
type FizzBuzzEnumerate<T> = EnumerateFromZeroWithCycle5<EnumerateFromZeroWithCycle3<T>>; | |
struct Fizz; | |
struct Buzz; | |
struct FizzBuzz; | |
trait ToFizzBuzz { | |
type Output; | |
} | |
type FizzBuzzed<T> = <T as ToFizzBuzz>::Output; | |
impl<T> ToFizzBuzz for ((T, Z), Z) { | |
type Output = FizzBuzz; | |
} | |
impl<T, N> ToFizzBuzz for ((T, Z), S<N>) { | |
type Output = Fizz; | |
} | |
impl<T, N> ToFizzBuzz for ((T, S<N>), Z) { | |
type Output = Buzz; | |
} | |
impl<T, N, M> ToFizzBuzz for ((T, S<N>), S<M>) { | |
type Output = T; | |
} | |
impl ToFizzBuzz for Nil { | |
type Output = Nil; | |
} | |
impl<Head, Tail> ToFizzBuzz for Cons<Head, Tail> | |
where | |
Head: ToFizzBuzz, | |
Tail: ToFizzBuzz, | |
{ | |
type Output = Cons<Head::Output, Tail::Output>; | |
} | |
trait TailOf { | |
type Output; | |
} | |
type Tail<T> = <T as TailOf>::Output; | |
impl<Head, Tail> TailOf for Cons<Head, Tail> { | |
type Output = Tail; | |
} | |
type FizzBuzzList<T> = FizzBuzzed<Tail<FizzBuzzEnumerate<RangeTo<T>>>>; | |
#[cfg(any(feature = "compare_with_previous_impl", not(feature = "use_nightly")))] | |
type List = FizzBuzzList<N>; | |
trait NumericValue { | |
const VALUE: usize; | |
} | |
impl NumericValue for Z { | |
const VALUE: usize = 0; | |
} | |
impl<N> NumericValue for S<N> | |
where | |
N: NumericValue, | |
{ | |
const VALUE: usize = N::VALUE + 1; | |
} | |
#[cfg(feature = "compare_with_previous_impl")] | |
mod old_impl { | |
use super::*; | |
const fn to_str<const N: usize>(mut value: usize) -> Str<{ N }> { | |
let mut s = [0; N]; | |
if value == 0 { | |
s[0] = b'0'; | |
return unsafe { Str::from_parts_unchecked(s, 1) }; | |
} | |
let mut i = 0; | |
while value > 0 { | |
s[i] = (value % 10) as u8 + b'0'; | |
value /= 10; | |
i += 1; | |
} | |
let n_digits = i; | |
let mut i = 0; | |
while i < n_digits / 2 { | |
let digit = s[n_digits - i - 1]; | |
s[n_digits - i - 1] = s[i]; | |
s[i] = digit; | |
i += 1; | |
} | |
unsafe { Str::from_parts_unchecked(s, n_digits) } | |
} | |
impl<T, const N: usize> AsStr<{ N }> for T | |
where | |
T: NumericValue, | |
{ | |
const VALUE: Str<{ N }> = to_str(<T as NumericValue>::VALUE); | |
} | |
pub trait AsStrList<const N: usize> { | |
type List; | |
const LIST: Self::List; | |
} | |
trait AsStr<const N: usize> { | |
const VALUE: Str<{ N }>; | |
} | |
impl<const N: usize> AsStr<{ N }> for Fizz { | |
const VALUE: Str<{ N }> = Str::from_literal("fizz"); | |
} | |
impl<const N: usize> AsStr<{ N }> for Buzz { | |
const VALUE: Str<{ N }> = Str::from_literal("buzz"); | |
} | |
impl<const N: usize> AsStr<{ N }> for FizzBuzz { | |
const VALUE: Str<{ N }> = Str::from_literal("fizzbuzz"); | |
} | |
impl<const N: usize> AsStrList<{ N }> for Nil { | |
type List = Nil; | |
const LIST: Nil = Nil; | |
} | |
impl<Head, Tail, const N: usize> AsStrList<{ N }> for Cons<Head, Tail> | |
where | |
Head: AsStr<{ N }>, | |
Tail: AsStrList<{ N }>, | |
{ | |
type List = Cons<Str<{ N }>, Tail::List>; | |
const LIST: Self::List = Cons( | |
<Head as AsStr<{ N }>>::VALUE, | |
<Tail as AsStrList<{ N }>>::LIST, | |
); | |
} | |
mod str { | |
pub struct Str<const N: usize>(StrInner<{ N }>); | |
enum StrInner<const N: usize> { | |
Plain(&'static str), | |
Decomposed([u8; N], usize), | |
} | |
impl<const N: usize> Str<{ N }> { | |
pub const fn from_literal(s: &'static str) -> Self { | |
Self(StrInner::Plain(s)) | |
} | |
pub const unsafe fn from_parts_unchecked(bytes: [u8; N], len: usize) -> Self { | |
Self(StrInner::Decomposed(bytes, len)) | |
} | |
pub fn as_str(&self) -> &str { | |
match &self.0 { | |
&StrInner::Plain(s) => s, | |
&StrInner::Decomposed(ref bytes, len) => unsafe { | |
std::str::from_utf8_unchecked(bytes.get_unchecked(..len)) | |
}, | |
} | |
} | |
} | |
} | |
use self::str::Str; | |
pub trait ForEach<Arg> { | |
fn for_each<F>(&self, f: F) | |
where | |
F: FnMut(&Arg); | |
} | |
impl<Arg> ForEach<Arg> for Nil { | |
fn for_each<F>(&self, _f: F) | |
where | |
F: FnMut(&Arg), | |
{ | |
} | |
} | |
impl<Arg, Tail> ForEach<Arg> for Cons<Arg, Tail> | |
where | |
Tail: ForEach<Arg>, | |
{ | |
fn for_each<F>(&self, mut f: F) | |
where | |
F: FnMut(&Arg), | |
{ | |
f(&self.0); | |
self.1.for_each(f); | |
} | |
} | |
} | |
#[cfg(feature = "compare_with_previous_impl")] | |
use old_impl::*; | |
const fn n_digits(mut n: usize) -> usize { | |
if n == 0 { | |
return 1; | |
} | |
let mut ret = 0; | |
while n > 0 { | |
n /= 10; | |
ret += 1; | |
} | |
ret | |
} | |
const fn write_str_at<const N: usize>( | |
s: &str, | |
mut bytes: [u8; N], | |
mut at: usize, | |
) -> ([u8; N], usize) { | |
let mut i = s.len(); | |
let s = s.as_bytes(); | |
while i > 0 { | |
bytes[at - 1] = s[i - 1]; | |
at -= 1; | |
i -= 1; | |
} | |
(bytes, at) | |
} | |
const DELIMITER: &str = "\n"; | |
const fn write_str_with_delimiter_at<const N: usize>( | |
s: &str, | |
mut bytes: [u8; N], | |
mut at: usize, | |
) -> ([u8; N], usize) { | |
(bytes, at) = write_str_at(DELIMITER, bytes, at); | |
write_str_at(s, bytes, at) | |
} | |
const fn write_num_with_delimiter_at<const N: usize>( | |
mut n: usize, | |
mut bytes: [u8; N], | |
mut at: usize, | |
) -> ([u8; N], usize) { | |
(bytes, at) = write_str_at(DELIMITER, bytes, at); | |
if n == 0 { | |
bytes[at - 1] = b'0'; | |
at -= 1; | |
return (bytes, at); | |
} | |
while n != 0 { | |
bytes[at - 1] = (n % 10) as u8 + b'0'; | |
at -= 1; | |
n /= 10; | |
} | |
(bytes, at) | |
} | |
enum Repr { | |
Direct(&'static str), | |
Numeric(usize), | |
} | |
trait AsRepr { | |
const REPR: Repr; | |
} | |
impl<T: NumericValue> AsRepr for T { | |
const REPR: Repr = Repr::Numeric(T::VALUE); | |
} | |
impl AsRepr for Fizz { | |
const REPR: Repr = Repr::Direct("fizz"); | |
} | |
impl AsRepr for Buzz { | |
const REPR: Repr = Repr::Direct("buzz"); | |
} | |
impl AsRepr for FizzBuzz { | |
const REPR: Repr = Repr::Direct("fizzbuzz"); | |
} | |
trait WriteLen { | |
const LEN: usize; | |
} | |
impl WriteLen for Nil { | |
const LEN: usize = 0; | |
} | |
impl<Head, Tail> WriteLen for Cons<Head, Tail> | |
where | |
Head: AsRepr, | |
Tail: WriteLen, | |
{ | |
const LEN: usize = Tail::LEN | |
+ DELIMITER.len() | |
+ match Head::REPR { | |
Repr::Direct(s) => s.len(), | |
Repr::Numeric(n) => n_digits(n), | |
}; | |
} | |
trait WriteBuf<const N: usize> { | |
const BUF: ([u8; N], usize); | |
} | |
impl<const N: usize> WriteBuf<{ N }> for Nil { | |
const BUF: ([u8; N], usize) = ([0; N], N); | |
} | |
impl<Head, Tail, const N: usize> WriteBuf<{ N }> for Cons<Head, Tail> | |
where | |
Head: AsRepr, | |
Tail: WriteBuf<{ N }>, | |
{ | |
const BUF: ([u8; N], usize) = { | |
let (bytes, at) = Tail::BUF; | |
match Head::REPR { | |
Repr::Direct(s) => write_str_with_delimiter_at(s, bytes, at), | |
Repr::Numeric(n) => write_num_with_delimiter_at(n, bytes, at), | |
} | |
}; | |
} | |
#[cfg(feature = "use_nightly")] | |
const fn to_buf<N>() -> ([u8; FizzBuzzList::<N>::LEN], usize) | |
where | |
N: RangeDownFrom, | |
MakeRangeDownFrom<N>: ReverseWith<Nil>, | |
RangeTo<N>: EnumerateFromWithCycle<Z, Three>, | |
EnumerateFromZeroWithCycle3<RangeTo<N>>: EnumerateFromWithCycle<Z, Five>, | |
FizzBuzzEnumerate<RangeTo<N>>: TailOf, | |
Tail<FizzBuzzEnumerate<RangeTo<N>>>: ToFizzBuzz, | |
FizzBuzzList<N>: WriteLen + WriteBuf<{ FizzBuzzList::<N>::LEN }>, | |
{ | |
<FizzBuzzList::<N> as WriteBuf<{ FizzBuzzList::<N>::LEN }>>::BUF | |
} | |
const FIZZ_BUZZ_BYTES: &[u8] = &{ | |
#[cfg(not(feature = "use_nightly"))] | |
let (precalculated, at) = { | |
const LIST_WRITE_LEN: usize = <List as WriteLen>::LEN; | |
<List as WriteBuf<LIST_WRITE_LEN>>::BUF | |
}; | |
#[cfg(feature = "use_nightly")] | |
let (precalculated, at) = to_buf::<N>(); | |
assert!(at == 0); | |
precalculated | |
}; | |
#[cfg(feature = "compare_with_previous_impl")] | |
const FIZZ_BUZZ_STR: &str = unsafe { std::str::from_utf8_unchecked(FIZZ_BUZZ_BYTES) }; | |
fn main() { | |
use std::io::Write; | |
#[cfg(feature = "compare_with_previous_impl")] | |
{ | |
let mut printed = String::new(); | |
<List as AsStrList<{ n_digits(<N as NumericValue>::VALUE) }>>::LIST.for_each(|s| { | |
printed += s.as_str(); | |
printed += DELIMITER; | |
}); | |
assert_eq!(printed.as_bytes(), FIZZ_BUZZ_STR); | |
} | |
std::io::stdout().write_all(FIZZ_BUZZ_BYTES).unwrap(); | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment