1use std::ops::{Deref, DerefMut};
5
6use super::{AmxCell, Ref};
7use crate::amx::Amx;
8use crate::cell::repr::CellConvert;
9use crate::cell::string;
10use crate::error::AmxResult;
11
12pub struct Buffer<'amx> {
32 inner: Ref<'amx, i32>,
33 len: usize,
34}
35
36impl<'amx> Buffer<'amx> {
37 #[must_use]
39 pub fn new(reference: Ref<'amx, i32>, len: usize) -> Buffer<'amx> {
40 Buffer {
41 inner: reference,
42 len,
43 }
44 }
45
46 #[must_use]
48 pub fn len(&self) -> usize {
49 self.len
50 }
51
52 #[must_use]
54 pub fn is_empty(&self) -> bool {
55 self.len == 0
56 }
57
58 #[inline]
60 #[must_use]
61 pub fn as_slice(&self) -> &[i32] {
62 unsafe { std::slice::from_raw_parts(self.inner.as_ptr(), self.len) }
63 }
64
65 #[inline]
67 pub fn as_mut_slice(&mut self) -> &mut [i32] {
68 unsafe { std::slice::from_raw_parts_mut(self.inner.as_mut_ptr(), self.len) }
69 }
70
71 pub fn iter_as<T: CellConvert>(&self) -> impl Iterator<Item = T> + '_ {
81 self.as_slice().iter().map(|&raw| T::from_cell(raw))
82 }
83
84 #[must_use]
86 pub fn get_as<T: CellConvert>(&self, index: usize) -> Option<T> {
87 self.as_slice().get(index).map(|&raw| T::from_cell(raw))
88 }
89
90 pub fn set_as<T: CellConvert>(&mut self, index: usize, value: T) -> bool {
94 if let Some(cell) = self.as_mut_slice().get_mut(index) {
95 *cell = value.into_cell();
96 true
97 } else {
98 false
99 }
100 }
101
102 pub fn write_str(&mut self, s: &str) -> AmxResult<()> {
109 string::put_in_buffer(self, s)
110 }
111}
112
113impl<'amx> AmxCell<'amx> for Buffer<'amx> {
116 #[inline]
117 fn as_cell(&self) -> i32 {
118 self.inner.as_cell()
119 }
120}
121
122impl Deref for Buffer<'_> {
123 type Target = [i32];
124
125 fn deref(&self) -> &[i32] {
126 self.as_slice()
127 }
128}
129
130impl DerefMut for Buffer<'_> {
131 fn deref_mut(&mut self) -> &mut [i32] {
132 self.as_mut_slice()
133 }
134}
135
136impl std::fmt::Debug for Buffer<'_> {
137 fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
138 write!(f, "{:?}", self.as_slice())
139 }
140}
141
142pub struct UnsizedBuffer<'amx> {
150 inner: Ref<'amx, i32>,
151 max_cells: usize,
158}
159
160impl<'amx> UnsizedBuffer<'amx> {
161 #[must_use]
170 pub fn into_sized_buffer(self, len: usize) -> Buffer<'amx> {
171 const MAX_BUFFER_CELLS: usize = 1024 * 1024;
172 debug_assert!(
173 len <= MAX_BUFFER_CELLS,
174 "into_sized_buffer() received len={len} above the {MAX_BUFFER_CELLS} limit"
175 );
176 let len = len.min(self.max_cells).min(MAX_BUFFER_CELLS);
177 Buffer::new(self.inner, len)
178 }
179
180 #[inline]
182 #[must_use]
183 pub fn as_ptr(&self) -> *const i32 {
184 self.inner.as_ptr()
185 }
186
187 #[inline]
189 pub fn as_mut_ptr(&mut self) -> *mut i32 {
190 self.inner.as_mut_ptr()
191 }
192
193 #[doc(hidden)]
195 #[must_use]
196 pub fn from_raw_parts(inner: Ref<'amx, i32>) -> Self {
197 UnsizedBuffer {
198 inner,
199 max_cells: usize::MAX,
200 }
201 }
202
203 pub fn write_str(self, max_len: usize, s: &str) -> AmxResult<()> {
212 let mut buf = self.into_sized_buffer(max_len);
213 string::put_in_buffer(&mut buf, s)
214 }
215}
216
217impl<'amx> AmxCell<'amx> for UnsizedBuffer<'amx> {
218 fn from_raw(amx: &'amx Amx, cell: i32) -> AmxResult<UnsizedBuffer<'amx>> {
219 let inner = amx.get_ref(cell)?;
220 let max_cells = amx.stp().map_or(usize::MAX, |stp| {
224 usize::try_from((stp - cell).max(0) / 4).unwrap_or(0)
225 });
226 Ok(UnsizedBuffer { inner, max_cells })
227 }
228
229 #[inline]
230 fn as_cell(&self) -> i32 {
231 self.inner.as_cell()
232 }
233}
234
235#[cfg(test)]
236mod tests {
237 use super::*;
238 use crate::cell::Ref;
239 use crate::cell::repr::CellConvert;
240
241 fn make_ref(data: &mut Vec<i32>) -> Ref<'_, i32> {
242 unsafe { Ref::new(0, data.as_mut_ptr()) }
243 }
244
245 fn make_buffer(data: &mut Vec<i32>) -> Buffer<'_> {
246 let len = data.len();
247 let r = make_ref(data);
248 Buffer::new(r, len)
249 }
250
251 fn make_unsized(data: &mut Vec<i32>) -> UnsizedBuffer<'_> {
252 UnsizedBuffer {
253 inner: make_ref(data),
254 max_cells: usize::MAX,
255 }
256 }
257
258 #[test]
261 fn buffer_len_and_is_empty() {
262 let mut data = vec![0i32; 4];
263 let buf = make_buffer(&mut data);
264 assert_eq!(buf.len(), 4);
265 assert!(!buf.is_empty());
266
267 let mut empty = vec![];
268 let empty_buf = make_buffer(&mut empty);
269 assert_eq!(empty_buf.len(), 0);
270 assert!(empty_buf.is_empty());
271 }
272
273 #[test]
274 fn buffer_deref_reads_values() {
275 let mut data = vec![10i32, 20, 30];
276 let buf = make_buffer(&mut data);
277 assert_eq!(&buf[..], &[10, 20, 30]);
278 assert_eq!(buf[0], 10);
279 assert_eq!(buf[2], 30);
280 }
281
282 #[test]
283 fn buffer_deref_mut_writes_values() {
284 let mut data = vec![0i32; 3];
285 let mut buf = make_buffer(&mut data);
286 buf[0] = 100;
287 buf[1] = 200;
288 buf[2] = 300;
289 assert_eq!(&data, &[100, 200, 300]);
290 }
291
292 #[test]
293 fn buffer_iter_works() {
294 let mut data = vec![1i32, 2, 3, 4];
295 let buf = make_buffer(&mut data);
296 let sum: i32 = buf.iter().sum();
297 assert_eq!(sum, 10);
298 }
299
300 #[test]
301 fn buffer_iter_mut_modifies_in_place() {
302 let mut data = vec![1i32, 2, 3];
303 let mut buf = make_buffer(&mut data);
304 buf.iter_mut().for_each(|x| *x *= 2);
305 assert_eq!(&data, &[2, 4, 6]);
306 }
307
308 #[test]
309 fn buffer_debug_format() {
310 let mut data = vec![1i32, 2, 3];
311 let buf = make_buffer(&mut data);
312 assert_eq!(format!("{buf:?}"), "[1, 2, 3]");
313 }
314
315 #[test]
316 fn buffer_as_cell_returns_amx_addr() {
317 let mut data = vec![0i32; 4];
318 let buf = make_buffer(&mut data);
319 assert_eq!(buf.as_cell(), 0);
321 }
322
323 #[test]
326 fn unsized_into_sized_normal_len() {
327 let mut data = vec![1i32, 2, 3, 4, 5];
328 let ub = make_unsized(&mut data);
329 let buf = ub.into_sized_buffer(3);
330 assert_eq!(buf.len(), 3);
331 assert_eq!(buf[0], 1);
332 assert_eq!(buf[2], 3);
333 }
334
335 #[test]
338 #[cfg_attr(
339 debug_assertions,
340 should_panic(expected = "into_sized_buffer() received len=")
341 )]
342 fn unsized_into_sized_clamps_to_max_in_release() {
343 let mut data = vec![0i32; 8];
344 let ub = make_unsized(&mut data);
345 let buf = ub.into_sized_buffer(1024 * 1024 + 1);
346 assert_eq!(buf.len(), 1024 * 1024);
348 }
349
350 #[test]
351 fn unsized_into_sized_at_exact_max() {
352 let mut data = vec![0i32; 8];
353 let ub = make_unsized(&mut data);
354 let buf = ub.into_sized_buffer(1024 * 1024);
355 assert_eq!(buf.len(), 1024 * 1024);
356 }
357
358 #[test]
359 fn into_sized_clamps_to_segment_bound() {
360 let mut data = vec![0i32; 8];
363 let ub = UnsizedBuffer {
364 inner: make_ref(&mut data),
365 max_cells: 3,
366 };
367 let buf = ub.into_sized_buffer(1000);
368 assert_eq!(buf.len(), 3);
369 }
370
371 #[test]
372 fn unsized_as_ptr_not_null() {
373 let mut data = vec![42i32];
374 let ub = make_unsized(&mut data);
375 assert!(!ub.as_ptr().is_null());
376 }
377
378 #[test]
379 fn unsized_as_cell_returns_amx_addr() {
380 let mut data = vec![0i32];
381 let ub = make_unsized(&mut data);
382 assert_eq!(ub.as_cell(), 0);
383 }
384
385 #[test]
388 fn get_as_i32_reads_value() {
389 let mut data = vec![10i32, 20, 30];
390 let buf = make_buffer(&mut data);
391 assert_eq!(buf.get_as::<i32>(0), Some(10));
392 assert_eq!(buf.get_as::<i32>(2), Some(30));
393 }
394
395 #[test]
396 fn get_as_out_of_bounds_returns_none() {
397 let mut data = vec![1i32, 2];
398 let buf = make_buffer(&mut data);
399 assert_eq!(buf.get_as::<i32>(2), None);
400 assert_eq!(buf.get_as::<i32>(99), None);
401 }
402
403 #[test]
404 fn set_as_i32_writes_value() {
405 let mut data = vec![0i32; 3];
406 let mut buf = make_buffer(&mut data);
407 assert!(buf.set_as(1, 42i32));
408 assert_eq!(data[1], 42);
409 }
410
411 #[test]
412 fn set_as_out_of_bounds_returns_false() {
413 let mut data = vec![0i32; 2];
414 let mut buf = make_buffer(&mut data);
415 assert!(!buf.set_as(5, 99i32));
416 }
417
418 #[test]
419 fn get_as_f32_roundtrip() {
420 let value = 1.5f32; let mut data = vec![value.into_cell()];
422 let buf = make_buffer(&mut data);
423 let recovered: f32 = buf.get_as::<f32>(0).unwrap();
424 assert!(
425 (recovered - value).abs() < f32::EPSILON,
426 "f32 roundtrip failed: {recovered} != {value}"
427 );
428 }
429
430 #[test]
431 fn set_as_f32_stores_bits_correctly() {
432 let mut data = vec![0i32];
433 let mut buf = make_buffer(&mut data);
434 buf.set_as(0, 1.5f32);
435 assert_eq!(data[0].cast_unsigned(), 1.5f32.to_bits());
436 }
437
438 #[test]
439 fn get_as_bool_true_and_false() {
440 let mut data = vec![1i32, 0, 42];
441 let buf = make_buffer(&mut data);
442 assert_eq!(buf.get_as::<bool>(0), Some(true));
443 assert_eq!(buf.get_as::<bool>(1), Some(false));
444 assert_eq!(buf.get_as::<bool>(2), Some(true));
446 }
447
448 #[test]
449 fn set_as_bool_writes_zero_and_one() {
450 let mut data = vec![0i32; 2];
451 let mut buf = make_buffer(&mut data);
452 buf.set_as(0, true);
453 buf.set_as(1, false);
454 assert_eq!(data[0], 1);
455 assert_eq!(data[1], 0);
456 }
457
458 #[test]
459 fn get_as_u8_reads_byte() {
460 let mut data = vec![255i32];
461 let buf = make_buffer(&mut data);
462 assert_eq!(buf.get_as::<u8>(0), Some(255u8));
463 }
464
465 #[test]
468 fn iter_as_i32_collects_all() {
469 let mut data = vec![1i32, 2, 3, 4];
470 let buf = make_buffer(&mut data);
471 let vals: Vec<i32> = buf.iter_as::<i32>().collect();
472 assert_eq!(vals, vec![1, 2, 3, 4]);
473 }
474
475 #[test]
476 fn iter_as_i32_sum() {
477 let mut data = vec![10i32, 20, 30];
478 let buf = make_buffer(&mut data);
479 let sum: i32 = buf.iter_as::<i32>().sum();
480 assert_eq!(sum, 60);
481 }
482
483 #[test]
484 fn iter_as_f32_roundtrip() {
485 let values = [1.0f32, 2.5, 1.25];
486 let mut data: Vec<i32> = values.iter().map(|&v| v.into_cell()).collect();
487 let buf = make_buffer(&mut data);
488 let recovered: Vec<f32> = buf.iter_as::<f32>().collect();
489 for (orig, got) in values.iter().zip(recovered.iter()) {
490 assert!((orig - got).abs() < f32::EPSILON, "{orig} != {got}");
491 }
492 }
493
494 #[test]
495 fn iter_as_bool_any_and_all() {
496 let mut data = vec![1i32, 0, 1, 1];
497 let buf = make_buffer(&mut data);
498 assert!(buf.iter_as::<bool>().any(|v| !v));
499 assert!(!buf.iter_as::<bool>().all(|v| v));
500 }
501
502 #[test]
503 fn iter_as_empty_buffer() {
504 let mut data: Vec<i32> = vec![];
505 let buf = make_buffer(&mut data);
506 assert_eq!(buf.iter_as::<i32>().count(), 0);
507 }
508
509 #[test]
510 fn iter_as_matches_get_as_loop() {
511 let mut data = vec![10i32, 20, 30, 40];
512 let buf = make_buffer(&mut data);
513 let via_iter: Vec<i32> = buf.iter_as::<i32>().collect();
514 let via_loop: Vec<i32> = (0..buf.len())
515 .filter_map(|i| buf.get_as::<i32>(i))
516 .collect();
517 assert_eq!(via_iter, via_loop);
518 }
519
520 #[test]
523 fn write_str_encodes_string_into_cells() {
524 let mut data = vec![0i32; 3];
526 let mut buf = make_buffer(&mut data);
527 assert!(buf.write_str("hi").is_ok());
528 assert_eq!(data[0], i32::from(b'h'));
529 assert_eq!(data[1], i32::from(b'i'));
530 assert_eq!(data[2], 0); }
532
533 #[test]
534 fn write_str_empty_string_writes_null_terminator() {
535 let mut data = vec![99i32; 2];
536 let mut buf = make_buffer(&mut data);
537 assert!(buf.write_str("").is_ok());
538 assert_eq!(data[0], 0);
539 }
540
541 #[test]
542 fn write_str_exact_fit_fails() {
543 let mut data = vec![0i32; 3];
545 let mut buf = make_buffer(&mut data);
546 assert!(buf.write_str("abc").is_err());
547 }
548
549 #[test]
552 fn unsized_write_str_sizes_and_writes() {
553 let mut data = vec![0i32; 5];
554 let ub = make_unsized(&mut data);
555 assert!(ub.write_str(5, "hi").is_ok());
556 assert_eq!(data[0], i32::from(b'h'));
557 assert_eq!(data[1], i32::from(b'i'));
558 assert_eq!(data[2], 0);
559 }
560
561 #[test]
562 fn unsized_write_str_too_long_returns_err() {
563 let mut data = vec![0i32; 3];
564 let ub = make_unsized(&mut data);
565 assert!(ub.write_str(3, "abc").is_err());
566 }
567
568 fn lcg(seed: &mut u64) -> u32 {
572 *seed = seed
573 .wrapping_mul(6_364_136_223_846_793_005)
574 .wrapping_add(1_442_695_040_888_963_407);
575 (*seed >> 33) as u32
576 }
577
578 #[test]
579 fn fuzz_get_set_as_respects_bounds() {
580 let mut seed = 0xDEAD_C0DE_1234_5678u64;
581 for _ in 0..4000 {
582 let len = (lcg(&mut seed) % 10) as usize; let mut data: Vec<i32> = vec![0; len];
584 let mut buf = make_buffer(&mut data);
585 let idx = (lcg(&mut seed) % 32) as usize; assert_eq!(buf.get_as::<i32>(idx).is_some(), idx < len);
589 let wrote = buf.set_as::<i32>(idx, 7);
591 assert_eq!(wrote, idx < len);
592 if wrote {
593 assert_eq!(buf.get_as::<i32>(idx), Some(7));
594 }
595 }
596 }
597
598 #[test]
599 fn fuzz_into_sized_never_exceeds_declared_len() {
600 let mut seed = 0x00C0_FFEE_BADD_F00Du64;
603 for _ in 0..2000 {
604 let cells = (lcg(&mut seed) % 8) as usize + 1;
605 let mut data: Vec<i32> = vec![0; cells];
606 let bound = (lcg(&mut seed) % 16) as usize;
607 let ub = UnsizedBuffer {
608 inner: make_ref(&mut data),
609 max_cells: bound,
610 };
611 let requested = (lcg(&mut seed) % 1000) as usize;
612 let buf = ub.into_sized_buffer(requested);
613 assert_eq!(buf.len(), requested.min(bound).min(1024 * 1024));
615 }
616 }
617}