1use super::cell::{Cell, CellFlags, Rgb};
7
8#[derive(Clone)]
19pub struct Buffer {
20 cells: Vec<Cell>,
22 width: u16,
24 height: u16,
26 overflow: Vec<String>,
28}
29
30impl Buffer {
31 pub fn new(width: u16, height: u16) -> Self {
38 assert!(width > 0 && height > 0, "Buffer dimensions must be non-zero");
39 let size = (width as usize) * (height as usize);
40 Self {
41 cells: vec![Cell::EMPTY; size],
42 width,
43 height,
44 overflow: Vec::new(),
45 }
46 }
47
48 #[inline]
50 pub const fn width(&self) -> u16 {
51 self.width
52 }
53
54 #[inline]
56 pub const fn height(&self) -> u16 {
57 self.height
58 }
59
60 #[inline]
62 pub const fn len(&self) -> usize {
63 self.cells.len()
64 }
65
66 #[inline]
68 pub const fn is_empty(&self) -> bool {
69 self.cells.is_empty()
70 }
71
72 #[inline]
74 pub fn cells(&self) -> &[Cell] {
75 &self.cells
76 }
77
78 #[inline]
80 pub fn cells_mut(&mut self) -> &mut [Cell] {
81 &mut self.cells
82 }
83
84 #[inline]
88 pub const fn index_of(&self, x: u16, y: u16) -> Option<usize> {
89 if x < self.width && y < self.height {
90 Some((y as usize) * (self.width as usize) + (x as usize))
91 } else {
92 None
93 }
94 }
95
96 #[inline]
98 #[allow(clippy::cast_possible_truncation)]
99 pub const fn coords_of(&self, index: usize) -> Option<(u16, u16)> {
100 if index < self.cells.len() {
101 let x = (index % (self.width as usize)) as u16;
102 let y = (index / (self.width as usize)) as u16;
103 Some((x, y))
104 } else {
105 None
106 }
107 }
108
109 #[inline]
113 pub fn get(&self, x: u16, y: u16) -> Option<&Cell> {
114 self.index_of(x, y).map(|i| &self.cells[i])
115 }
116
117 #[inline]
121 pub fn get_mut(&mut self, x: u16, y: u16) -> Option<&mut Cell> {
122 self.index_of(x, y).map(|i| &mut self.cells[i])
123 }
124
125 #[inline]
129 pub fn set(&mut self, x: u16, y: u16, cell: Cell) -> bool {
130 if let Some(idx) = self.index_of(x, y) {
131 self.cells[idx] = cell;
132 true
133 } else {
134 false
135 }
136 }
137
138 pub fn set_grapheme(&mut self, x: u16, y: u16, grapheme: &str, fg: Rgb, bg: Rgb) -> u8 {
145 let Some(idx) = self.index_of(x, y) else {
146 return 0;
147 };
148
149 let width = u8::try_from(unicode_width::UnicodeWidthStr::width(grapheme)).unwrap_or(1);
150
151 let cell = if let Some(mut cell) = Cell::from_grapheme(grapheme) {
153 cell.set_fg(fg).set_bg(bg);
154 cell
155 } else {
156 #[allow(clippy::cast_possible_truncation)]
159 let overflow_idx = self.overflow.len() as u32;
160 self.overflow.push(grapheme.to_string());
161 Cell::overflow(overflow_idx, width).with_fg(fg).with_bg(bg)
162 };
163
164 self.cells[idx] = cell;
165
166 if width == 2 {
168 if let Some(next_idx) = self.index_of(x + 1, y) {
169 self.cells[next_idx] = Cell::wide_continuation().with_bg(bg);
170 }
171 }
172
173 width
174 }
175
176 pub fn get_grapheme(&self, x: u16, y: u16) -> Option<&str> {
180 let cell = self.get(x, y)?;
181
182 if cell.is_wide_continuation() {
183 return None;
184 }
185
186 if cell.flags().contains(CellFlags::OVERFLOW) {
187 let idx = cell.overflow_index()?;
188 self.overflow.get(idx as usize).map(String::as_str)
189 } else {
190 cell.grapheme()
191 }
192 }
193
194 #[inline]
198 pub fn get_overflow(&self, index: u32) -> Option<&str> {
199 self.overflow.get(index as usize).map(String::as_str)
200 }
201
202 pub fn fill_rect(&mut self, x: u16, y: u16, width: u16, height: u16, cell: Cell) {
204 for row in y..(y + height).min(self.height) {
205 for col in x..(x + width).min(self.width) {
206 if let Some(idx) = self.index_of(col, row) {
207 self.cells[idx] = cell;
208 }
209 }
210 }
211 }
212
213 pub fn clear(&mut self) {
215 self.cells.fill(Cell::EMPTY);
216 self.overflow.clear();
217 }
218
219 pub fn clear_rect(&mut self, x: u16, y: u16, width: u16, height: u16) {
221 self.fill_rect(x, y, width, height, Cell::EMPTY);
222 }
223
224 pub fn resize(&mut self, new_width: u16, new_height: u16) {
228 if new_width == self.width && new_height == self.height {
229 return;
230 }
231
232 let new_size = (new_width as usize) * (new_height as usize);
233 let mut new_cells = vec![Cell::EMPTY; new_size];
234
235 let copy_width = self.width.min(new_width) as usize;
237 let copy_height = self.height.min(new_height) as usize;
238
239 for y in 0..copy_height {
240 let old_start = y * (self.width as usize);
241 let new_start = y * (new_width as usize);
242 new_cells[new_start..new_start + copy_width]
243 .copy_from_slice(&self.cells[old_start..old_start + copy_width]);
244 }
245
246 self.cells = new_cells;
247 self.width = new_width;
248 self.height = new_height;
249 }
250
251 pub fn copy_from(&mut self, other: &Self) {
255 debug_assert_eq!(self.width, other.width);
256 debug_assert_eq!(self.height, other.height);
257 self.cells.copy_from_slice(&other.cells);
258 self.overflow.clone_from(&other.overflow);
259 }
260
261 pub const fn swap(&mut self, other: &mut Self) {
265 std::mem::swap(&mut self.cells, &mut other.cells);
266 std::mem::swap(&mut self.width, &mut other.width);
267 std::mem::swap(&mut self.height, &mut other.height);
268 std::mem::swap(&mut self.overflow, &mut other.overflow);
269 }
270
271 pub fn rows(&self) -> impl Iterator<Item = &[Cell]> {
273 self.cells.chunks(self.width as usize)
274 }
275
276 pub fn rows_mut(&mut self) -> impl Iterator<Item = &mut [Cell]> {
278 self.cells.chunks_mut(self.width as usize)
279 }
280
281 pub fn memory_usage(&self) -> usize {
283 let cells_size = self.cells.len() * std::mem::size_of::<Cell>();
284 let overflow_size: usize = self.overflow.iter().map(|s| s.len() + 32).sum();
285 cells_size + overflow_size + std::mem::size_of::<Self>()
286 }
287}
288
289impl std::fmt::Debug for Buffer {
290 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
291 f.debug_struct("Buffer")
292 .field("width", &self.width)
293 .field("height", &self.height)
294 .field("overflow_count", &self.overflow.len())
295 .field("memory_bytes", &self.memory_usage())
296 .finish_non_exhaustive()
297 }
298}
299
300#[cfg(test)]
301mod tests {
302 use super::*;
303
304 #[test]
305 fn test_buffer_new() {
306 let buffer = Buffer::new(80, 24);
307 assert_eq!(buffer.width(), 80);
308 assert_eq!(buffer.height(), 24);
309 assert_eq!(buffer.len(), 80 * 24);
310 }
311
312 #[test]
313 #[should_panic]
314 fn test_buffer_zero_width() {
315 Buffer::new(0, 24);
316 }
317
318 #[test]
319 fn test_buffer_get_set() {
320 let mut buffer = Buffer::new(80, 24);
321 let cell = Cell::new('X');
322 assert!(buffer.set(5, 10, cell));
323 assert_eq!(buffer.get(5, 10).unwrap().grapheme(), Some("X"));
324 }
325
326 #[test]
327 fn test_buffer_bounds() {
328 let buffer = Buffer::new(80, 24);
329 assert!(buffer.get(79, 23).is_some());
330 assert!(buffer.get(80, 23).is_none());
331 assert!(buffer.get(79, 24).is_none());
332 }
333
334 #[test]
335 fn test_buffer_index_coords() {
336 let buffer = Buffer::new(80, 24);
337 assert_eq!(buffer.index_of(5, 10), Some(10 * 80 + 5));
338 assert_eq!(buffer.coords_of(10 * 80 + 5), Some((5, 10)));
339 }
340
341 #[test]
342 fn test_buffer_set_grapheme() {
343 let mut buffer = Buffer::new(80, 24);
344
345 let width = buffer.set_grapheme(0, 0, "A", Rgb::WHITE, Rgb::BLACK);
347 assert_eq!(width, 1);
348 assert_eq!(buffer.get_grapheme(0, 0), Some("A"));
349
350 let width = buffer.set_grapheme(5, 0, "ๆฅ", Rgb::WHITE, Rgb::BLACK);
352 assert_eq!(width, 2);
353 assert_eq!(buffer.get_grapheme(5, 0), Some("ๆฅ"));
354 assert!(buffer.get(6, 0).unwrap().is_wide_continuation());
356 }
357
358 #[test]
359 fn test_buffer_overflow() {
360 let mut buffer = Buffer::new(80, 24);
361
362 let emoji = "๐จโ๐ฉโ๐งโ๐ฆ";
364 let width = buffer.set_grapheme(0, 0, emoji, Rgb::WHITE, Rgb::BLACK);
365 assert!(width > 0);
366 assert!(buffer.get(0, 0).unwrap().is_overflow());
367 assert_eq!(buffer.get_grapheme(0, 0), Some(emoji));
368 }
369
370 #[test]
371 fn test_buffer_fill_rect() {
372 let mut buffer = Buffer::new(80, 24);
373 let cell = Cell::new('X');
374 buffer.fill_rect(10, 5, 3, 2, cell);
375
376 assert_eq!(buffer.get(10, 5).unwrap().grapheme(), Some("X"));
377 assert_eq!(buffer.get(11, 5).unwrap().grapheme(), Some("X"));
378 assert_eq!(buffer.get(12, 5).unwrap().grapheme(), Some("X"));
379 assert_eq!(buffer.get(10, 6).unwrap().grapheme(), Some("X"));
380 assert_eq!(buffer.get(9, 5).unwrap().grapheme(), Some(" ")); }
382
383 #[test]
384 fn test_buffer_clear() {
385 let mut buffer = Buffer::new(80, 24);
386 buffer.set(5, 5, Cell::new('X'));
387 buffer.clear();
388 assert_eq!(buffer.get(5, 5), Some(&Cell::EMPTY));
389 }
390
391 #[test]
392 fn test_buffer_resize() {
393 let mut buffer = Buffer::new(80, 24);
394 buffer.set(5, 5, Cell::new('X'));
395
396 buffer.resize(100, 30);
397 assert_eq!(buffer.width(), 100);
398 assert_eq!(buffer.height(), 30);
399 assert_eq!(buffer.get(5, 5).unwrap().grapheme(), Some("X")); buffer.resize(10, 10);
402 assert_eq!(buffer.get(5, 5).unwrap().grapheme(), Some("X")); assert!(buffer.get(15, 15).is_none()); }
405
406 #[test]
407 fn test_buffer_swap() {
408 let mut a = Buffer::new(80, 24);
409 let mut b = Buffer::new(80, 24);
410
411 a.set(0, 0, Cell::new('A'));
412 b.set(0, 0, Cell::new('B'));
413
414 a.swap(&mut b);
415
416 assert_eq!(a.get(0, 0).unwrap().grapheme(), Some("B"));
417 assert_eq!(b.get(0, 0).unwrap().grapheme(), Some("A"));
418 }
419
420 #[test]
421 fn test_buffer_memory_usage() {
422 let buffer = Buffer::new(200, 50);
423 let usage = buffer.memory_usage();
424 assert!(usage >= 160_000);
426 assert!(usage < 200_000); }
428}