1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
//
// Copyright (c) 2023 ZettaScale Technology
//
// This program and the accompanying materials are made available under the
// terms of the Eclipse Public License 2.0 which is available at
// http://www.eclipse.org/legal/epl-2.0, or the Apache License, Version 2.0
// which is available at https://www.apache.org/licenses/LICENSE-2.0.
//
// SPDX-License-Identifier: EPL-2.0 OR Apache-2.0
//
// Contributors:
//   Pierre Avital, <pierre.avital@me.com>
//

use crate::{str::Str, StableLike};
use sha2_const_stable::Sha256;

/// A type
type NextField = StableLike<Option<&'static FieldReport>, usize>;

/// A report of a type's layout.
#[crate::stabby]
#[derive(Debug, PartialEq, Eq, Clone, Copy)]
pub struct TypeReport {
    /// The type's name.
    pub name: Str<'static>,
    /// The type's parent module's path.
    pub module: Str<'static>,
    /// The fields of this type.
    pub fields: NextField,
    /// How the type was declared
    pub tyty: TyTy,
    /// The version of the type's invariants.
    pub version: u32,
}

impl core::fmt::Display for TypeReport {
    fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
        let Self {
            name,
            module,
            version,
            tyty,
            ..
        } = self;
        write!(f, "{tyty:?} {module} :: {name} (version{version}) {{")?;
        for FieldReport { name, ty, .. } in self.fields() {
            write!(f, "{name}: {ty}, ")?
        }
        write!(f, "}}")
    }
}
impl core::hash::Hash for TypeReport {
    fn hash<H: core::hash::Hasher>(&self, state: &mut H) {
        self.name.hash(state);
        self.module.hash(state);
        for field in self.fields() {
            field.hash(state);
        }
        self.version.hash(state);
        self.tyty.hash(state);
    }
}

impl TypeReport {
    /// Whether or not two reports correspond to the same type, with the same layout and invariants.
    pub fn is_compatible(&self, other: &Self) -> bool {
        self.name == other.name
            && self.module == other.module
            && self.version == other.version
            && self.tyty == other.tyty
            && self
                .fields()
                .zip(other.fields())
                .all(|(s, o)| s.name == o.name && s.ty.is_compatible(o.ty))
    }
}

/// How a type was declared.
#[crate::stabby]
#[repr(u8)]
#[derive(Debug, PartialEq, Eq, Clone, Copy, Hash)]
pub enum TyTy {
    /// As a struct
    Struct,
    /// As an enum (with which calling convention)
    Enum(Str<'static>),
    /// As a union.
    Union,
}

/// A type's field.
#[crate::stabby]
#[derive(Debug, PartialEq, Eq, Clone, Copy)]
pub struct FieldReport {
    /// The field's name.
    pub name: Str<'static>,
    /// The field;s type.
    pub ty: &'static TypeReport,
    /// The next field in the [`TypeReport`]
    pub next_field: NextField,
}
impl core::hash::Hash for FieldReport {
    fn hash<H: core::hash::Hasher>(&self, state: &mut H) {
        self.name.hash(state);
        self.ty.hash(state);
    }
}

impl TypeReport {
    /// Returns an iterator over the type's fields.
    pub const fn fields(&self) -> Fields {
        Fields(self.fields.value)
    }
}
/// An iterator over a type's fields.
#[crate::stabby]
pub struct Fields(Option<&'static FieldReport>);
impl Fields {
    /// A `const` compatible alternative to [`Iterator::next`]
    pub const fn next_const(self) -> (Self, Option<&'static FieldReport>) {
        match self.0 {
            Some(field) => (Self(*field.next_field.as_ref()), Some(field)),
            None => (self, None),
        }
    }
}
impl Iterator for Fields {
    type Item = &'static FieldReport;
    fn next(&mut self) -> Option<Self::Item> {
        let field = self.0.take()?;
        self.0 = field.next_field.value;
        Some(field)
    }
}

const fn hash_report(mut hash: Sha256, report: &TypeReport) -> Sha256 {
    hash = hash
        .update(report.module.as_str().as_bytes())
        .update(report.name.as_str().as_bytes());
    hash = match report.tyty {
        crate::report::TyTy::Struct => hash.update(&[0]),
        crate::report::TyTy::Union => hash.update(&[1]),
        crate::report::TyTy::Enum(s) => hash.update(s.as_str().as_bytes()),
    };
    let mut fields = report.fields();
    while let (new, Some(next)) = fields.next_const() {
        fields = new;
        hash = hash_report(hash.update(next.name.as_str().as_bytes()), next.ty)
    }
    hash
}
const ARCH_INFO: [u8; 8] = [
    0,
    core::mem::size_of::<usize>() as u8,
    core::mem::align_of::<usize>() as u8,
    core::mem::size_of::<&()>() as u8,
    core::mem::align_of::<&()>() as u8,
    core::mem::align_of::<u128>() as u8,
    unsafe { core::mem::transmute::<[u8; 2], u16>([0, 1]) } as u8,
    0,
];

/// Generates an ID based on a [`TypeReport`] using [`Sha256`].
///
/// The hash also contains information about the architecture, allowing to consider a same struct distinct depending
/// on the architecture's pointer size, alignment of u128, or endianness.
pub const fn gen_id(report: &TypeReport) -> u64 {
    let [hash @ .., _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _] =
        hash_report(Sha256::new(), report).finalize();
    u64::from_le_bytes(hash) ^ u64::from_le_bytes(ARCH_INFO)
}