1use indexset::core::node::NodeLike;
2use indexset::core::pair::Pair;
3use prettytable::{Table, format::consts::FORMAT_NO_BORDER_LINE_SEPARATOR, row};
4use std::fmt::{self, Debug, Display, Formatter};
5
6use crate::in_memory::{RowWrapper, StorableRow};
7use crate::mem_stat::MemStat;
8use crate::util::OffsetEqLink;
9use crate::{TableSecondaryIndexInfo, WorkTable};
10
11#[derive(Debug)]
12pub struct SystemInfo {
13 pub table_name: &'static str,
14 pub page_count: usize,
15 pub row_count: usize,
16 pub empty_slots: u64,
17 pub memory_usage_bytes: u64,
18 pub idx_size: usize,
19 pub indexes_info: Vec<IndexInfo>,
20}
21
22#[derive(Debug)]
23pub struct IndexInfo {
24 pub name: String,
25 pub index_type: IndexKind,
26 pub key_count: usize,
27 pub capacity: usize,
28 pub heap_size: usize,
29 pub used_size: usize,
30 pub node_count: usize,
31}
32
33#[derive(Debug)]
34pub enum IndexKind {
35 Unique,
36 NonUnique,
37}
38
39impl Display for IndexKind {
40 fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
41 match self {
42 Self::Unique => write!(f, "unique"),
43 Self::NonUnique => write!(f, "non unique"),
44 }
45 }
46}
47
48impl<
49 Row,
50 PrimaryKey,
51 AvailableTypes,
52 AvailableIndexes,
53 SecondaryIndexes,
54 LockType,
55 PkGen,
56 const DATA_LENGTH: usize,
57 NodeType,
58>
59 WorkTable<
60 Row,
61 PrimaryKey,
62 AvailableTypes,
63 AvailableIndexes,
64 SecondaryIndexes,
65 LockType,
66 PkGen,
67 DATA_LENGTH,
68 NodeType,
69 >
70where
71 PrimaryKey: Debug + Clone + Ord + Send + 'static + std::hash::Hash,
72 Row: StorableRow + Send + Clone + 'static,
73 <Row as StorableRow>::WrappedRow: RowWrapper<Row>,
74 NodeType: NodeLike<Pair<PrimaryKey, OffsetEqLink<DATA_LENGTH>>> + Send + 'static,
75 SecondaryIndexes: MemStat + TableSecondaryIndexInfo,
76{
77 pub fn system_info(&self) -> SystemInfo {
78 let page_count = self.data.get_page_count();
79 let row_count = self.primary_index.pk_map.len();
80
81 let empty_links = self.data.get_empty_links().len();
82
83 let bytes = self.data.get_bytes();
84
85 let memory_usage_bytes = bytes
86 .iter()
87 .map(|(_buf, free_offset)| *free_offset as u64)
88 .sum();
89
90 let idx_size = self.indexes.heap_size();
91
92 SystemInfo {
93 table_name: self.table_name,
94 page_count,
95 row_count,
96 empty_slots: empty_links as u64,
97 memory_usage_bytes,
98 idx_size,
99 indexes_info: self.indexes.index_info(),
100 }
101 }
102}
103
104impl Display for SystemInfo {
105 fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
106 let mem_fmt = fmt_bytes(self.memory_usage_bytes as usize);
107 let idx_fmt = fmt_bytes(self.idx_size);
108 let total_fmt = fmt_bytes(self.memory_usage_bytes as usize + self.idx_size);
109
110 writeln!(f, "┌──────────────────────────────┐")?;
111 writeln!(f, " \t Table Name: {:<5}", self.table_name)?;
112 writeln!(f, "└──────────────────────────────┘")?;
113 writeln!(
114 f,
115 "Rows: {} Pages: {} Empty slots: {}",
116 self.row_count, self.page_count, self.empty_slots
117 )?;
118 writeln!(
119 f,
120 "Allocated Memory: {mem_fmt} (data) + {idx_fmt} (indexes) = {total_fmt} total\n"
121 )?;
122
123 let mut table = Table::new();
124 table.set_format(*FORMAT_NO_BORDER_LINE_SEPARATOR);
125 table.add_row(row![
126 "Index",
127 "Type",
128 "Keys",
129 "Capacity",
130 "Node Count",
131 "Heap",
132 "Used"
133 ]);
134
135 for idx in &self.indexes_info {
136 table.add_row(row![
137 idx.name,
138 idx.index_type.to_string(),
139 idx.key_count,
140 idx.capacity,
141 idx.node_count,
142 fmt_bytes(idx.heap_size),
143 fmt_bytes(idx.used_size),
144 ]);
145 }
146
147 let mut buffer = Vec::new();
148 table.print(&mut buffer).unwrap();
149 let table_str = String::from_utf8(buffer).unwrap();
150 writeln!(f, "{}", table_str.trim_end())?;
151
152 Ok(())
153 }
154}
155
156fn fmt_bytes(bytes: usize) -> String {
157 const KB: f64 = 1024.0;
158 const MB: f64 = 1024.0 * KB;
159 const GB: f64 = 1024.0 * MB;
160
161 let b = bytes as f64;
162
163 let (value, unit) = if b >= GB {
164 (b / GB, "GB")
165 } else if b >= MB {
166 (b / MB, "MB")
167 } else if b >= KB {
168 (b / KB, "KB")
169 } else {
170 return format!("{bytes} B");
171 };
172
173 if (value.fract() * 100.0).round() == 0.0 {
174 format!("{value:.0} {unit}")
175 } else {
176 format!("{value:.2} {unit}")
177 }
178}