jvm_hprof/heap_dump/
primitive_array.rs

1use crate::*;
2
3use strum_macros;
4use strum_macros::EnumIter;
5
6/// An array of [JVM primitive types](https://docs.oracle.com/javase/specs/jvms/se15/html/jvms-2.html#jvms-2.3)
7/// (`int`, `long`, and so forth).
8///
9#[derive(CopyGetters)]
10pub struct PrimitiveArray<'a> {
11    #[get_copy = "pub"]
12    obj_id: Id,
13    #[get_copy = "pub"]
14    stack_trace_serial: Serial,
15    /// The type of primitive in the array.
16    ///
17    /// Methods for accessing the contents of the array (`ints()`, etc)  return `Some` for method
18    /// matching the array type and `None` otherwise (e.g. if it's a [PrimitiveArrayType::Float],
19    /// [PrimitiveArray::floats()] will return `Some` and all other accessors will return `None`.
20    #[get_copy = "pub"]
21    primitive_type: PrimitiveArrayType,
22    num_elements: u32,
23    contents: &'a [u8],
24}
25
26macro_rules! iterator_method {
27    ($method_name:tt, $type_variant:tt, $iter_struct:tt) => {
28        /// Returns `Some` if `primitive_type()` returns the matching variant and `None` otherwise.
29        pub fn $method_name(&self) -> Option<$iter_struct> {
30            match self.primitive_type {
31                PrimitiveArrayType::$type_variant => Some($iter_struct {
32                    iter: ParsingIterator::new_stateless(self.contents, self.num_elements),
33                }),
34                _ => None,
35            }
36        }
37    };
38}
39
40impl<'a> PrimitiveArray<'a> {
41    pub(crate) fn parse(input: &[u8], id_size: IdSize) -> nom::IResult<&[u8], PrimitiveArray> {
42        // https://github.com/openjdk/jdk/blob/08822b4e0526fe001c39fe08e241b849eddf481d/src/hotspot/share/services/heapDumper.cpp#L279
43        let (input, obj_id) = Id::parse(input, id_size)?;
44        let (input, stack_trace_serial) = number::be_u32(input)?;
45        let (input, num_elements) = number::be_u32(input)?;
46        let (input, type_byte) = number::be_u8(input)?;
47
48        let array_type = match PrimitiveArrayType::from_type_code(type_byte) {
49            Some(t) => t,
50            None => panic!("Unexpected primitive array type {:#X}", type_byte),
51        };
52
53        let size: u32 = match array_type {
54            PrimitiveArrayType::Boolean => 1,
55            PrimitiveArrayType::Char => 2,
56            PrimitiveArrayType::Float => 4,
57            PrimitiveArrayType::Double => 8,
58            PrimitiveArrayType::Byte => 1,
59            PrimitiveArrayType::Short => 2,
60            PrimitiveArrayType::Int => 4,
61            PrimitiveArrayType::Long => 8,
62        };
63
64        let (input, contents) = bytes::take(num_elements * size)(input)?;
65
66        Ok((
67            input,
68            PrimitiveArray {
69                obj_id,
70                stack_trace_serial: stack_trace_serial.into(),
71                primitive_type: array_type,
72                num_elements,
73                contents,
74            },
75        ))
76    }
77
78    iterator_method!(booleans, Boolean, Booleans);
79    iterator_method!(chars, Char, Chars);
80    iterator_method!(floats, Float, Floats);
81    iterator_method!(doubles, Double, Doubles);
82    iterator_method!(bytes, Byte, Bytes);
83    iterator_method!(shorts, Short, Shorts);
84    iterator_method!(ints, Int, Ints);
85    iterator_method!(longs, Long, Longs);
86}
87
88impl StatelessParser for bool {
89    fn parse(input: &[u8]) -> nom::IResult<&[u8], bool> {
90        number::be_u8(input).map(|(input, b)| (input, b != 0))
91    }
92}
93
94macro_rules! parser_impl {
95    ($prim_type:tt, $parser_method:tt) => {
96        impl StatelessParser for $prim_type {
97            fn parse(input: &[u8]) -> nom::IResult<&[u8], $prim_type> {
98                number::$parser_method(input).map(|(input, c)| (input, c))
99            }
100        }
101    };
102}
103
104parser_impl!(u16, be_u16);
105parser_impl!(f32, be_f32);
106parser_impl!(f64, be_f64);
107parser_impl!(i8, be_i8);
108parser_impl!(i16, be_i16);
109parser_impl!(i32, be_i32);
110parser_impl!(i64, be_i64);
111
112macro_rules! iter_struct {
113    ($struct_name:ident, $item_type:ty) => {
114        pub struct $struct_name<'a> {
115            iter: ParsingIterator<'a, $item_type, StatelessParserWrapper<$item_type>>,
116        }
117
118        impl<'a> Iterator for $struct_name<'a> {
119            type Item = ParseResult<'a, $item_type>;
120
121            fn next(&mut self) -> Option<Self::Item> {
122                self.iter.next()
123            }
124        }
125    };
126}
127
128iter_struct!(Booleans, bool);
129iter_struct!(Chars, u16);
130iter_struct!(Floats, f32);
131iter_struct!(Doubles, f64);
132iter_struct!(Bytes, i8);
133iter_struct!(Shorts, i16);
134iter_struct!(Ints, i32);
135iter_struct!(Longs, i64);
136
137#[derive(Clone, Copy, Debug, Hash, Eq, PartialEq, EnumIter)]
138pub enum PrimitiveArrayType {
139    Boolean,
140    Char,
141    Float,
142    Double,
143    Byte,
144    Short,
145    Int,
146    Long,
147}
148
149impl PrimitiveArrayType {
150    pub fn java_type_name(&self) -> &'static str {
151        match self {
152            PrimitiveArrayType::Boolean => "boolean",
153            PrimitiveArrayType::Char => "char",
154            PrimitiveArrayType::Float => "float",
155            PrimitiveArrayType::Double => "double",
156            PrimitiveArrayType::Byte => "byte",
157            PrimitiveArrayType::Short => "short",
158            PrimitiveArrayType::Int => "int",
159            PrimitiveArrayType::Long => "long",
160        }
161    }
162
163    /// Returns the hprof type code for the array type
164    ///
165    /// See https://github.com/openjdk/jdk/blob/08822b4e0526fe001c39fe08e241b849eddf481d/src/hotspot/share/services/heapDumper.cpp#L279
166    pub fn type_code(&self) -> u8 {
167        match self {
168            PrimitiveArrayType::Boolean => 0x04,
169            PrimitiveArrayType::Char => 0x05,
170            PrimitiveArrayType::Float => 0x06,
171            PrimitiveArrayType::Double => 0x07,
172            PrimitiveArrayType::Byte => 0x08,
173            PrimitiveArrayType::Short => 0x09,
174            PrimitiveArrayType::Int => 0x0A,
175            PrimitiveArrayType::Long => 0x0B,
176        }
177    }
178
179    /// Returns the type for the type code, or None if the code is unknown.
180    ///
181    /// See https://github.com/openjdk/jdk/blob/08822b4e0526fe001c39fe08e241b849eddf481d/src/hotspot/share/services/heapDumper.cpp#L279
182    pub fn from_type_code(type_byte: u8) -> Option<PrimitiveArrayType> {
183        match type_byte {
184            0x04 => Some(PrimitiveArrayType::Boolean),
185            0x05 => Some(PrimitiveArrayType::Char),
186            0x06 => Some(PrimitiveArrayType::Float),
187            0x07 => Some(PrimitiveArrayType::Double),
188            0x08 => Some(PrimitiveArrayType::Byte),
189            0x09 => Some(PrimitiveArrayType::Short),
190            0x0A => Some(PrimitiveArrayType::Int),
191            0x0B => Some(PrimitiveArrayType::Long),
192            _ => None,
193        }
194    }
195}