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 pub fn write_str_checked(&mut self, s: &str) -> AmxResult<bool> {
126 string::put_in_buffer_checked(self, s)
127 }
128}
129
130impl<'amx> AmxCell<'amx> for Buffer<'amx> {
133 #[inline]
134 fn as_cell(&self) -> i32 {
135 self.inner.as_cell()
136 }
137}
138
139impl Deref for Buffer<'_> {
140 type Target = [i32];
141
142 fn deref(&self) -> &[i32] {
143 self.as_slice()
144 }
145}
146
147impl DerefMut for Buffer<'_> {
148 fn deref_mut(&mut self) -> &mut [i32] {
149 self.as_mut_slice()
150 }
151}
152
153impl std::fmt::Debug for Buffer<'_> {
154 fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
155 write!(f, "{:?}", self.as_slice())
156 }
157}
158
159pub struct UnsizedBuffer<'amx> {
167 inner: Ref<'amx, i32>,
168 max_cells: usize,
175}
176
177impl<'amx> UnsizedBuffer<'amx> {
178 #[must_use]
187 pub fn into_sized_buffer(self, len: usize) -> Buffer<'amx> {
188 const MAX_BUFFER_CELLS: usize = 1024 * 1024;
189 debug_assert!(
190 len <= MAX_BUFFER_CELLS,
191 "into_sized_buffer() received len={len} above the {MAX_BUFFER_CELLS} limit"
192 );
193 let len = len.min(self.max_cells).min(MAX_BUFFER_CELLS);
194 Buffer::new(self.inner, len)
195 }
196
197 #[inline]
199 #[must_use]
200 pub fn as_ptr(&self) -> *const i32 {
201 self.inner.as_ptr()
202 }
203
204 #[inline]
206 pub fn as_mut_ptr(&mut self) -> *mut i32 {
207 self.inner.as_mut_ptr()
208 }
209
210 #[doc(hidden)]
212 #[must_use]
213 pub fn from_raw_parts(inner: Ref<'amx, i32>) -> Self {
214 UnsizedBuffer {
215 inner,
216 max_cells: usize::MAX,
217 }
218 }
219
220 pub fn write_str(self, max_len: usize, s: &str) -> AmxResult<()> {
229 let mut buf = self.into_sized_buffer(max_len);
230 string::put_in_buffer(&mut buf, s)
231 }
232
233 pub fn write_str_checked(self, max_len: usize, s: &str) -> AmxResult<bool> {
242 let mut buf = self.into_sized_buffer(max_len);
243 string::put_in_buffer_checked(&mut buf, s)
244 }
245}
246
247impl<'amx> AmxCell<'amx> for UnsizedBuffer<'amx> {
248 fn from_raw(amx: &'amx Amx, cell: i32) -> AmxResult<UnsizedBuffer<'amx>> {
249 let inner = amx.get_ref(cell)?;
250 let max_cells = amx.stp().map_or(usize::MAX, |stp| {
254 usize::try_from((stp - cell).max(0) / 4).unwrap_or(0)
255 });
256 Ok(UnsizedBuffer { inner, max_cells })
257 }
258
259 #[inline]
260 fn as_cell(&self) -> i32 {
261 self.inner.as_cell()
262 }
263}
264
265#[cfg(test)]
266mod tests {
267 use super::*;
268 use crate::cell::Ref;
269 use crate::cell::repr::CellConvert;
270
271 fn make_ref(data: &mut Vec<i32>) -> Ref<'_, i32> {
272 unsafe { Ref::new(0, data.as_mut_ptr()) }
273 }
274
275 fn make_buffer(data: &mut Vec<i32>) -> Buffer<'_> {
276 let len = data.len();
277 let r = make_ref(data);
278 Buffer::new(r, len)
279 }
280
281 fn make_unsized(data: &mut Vec<i32>) -> UnsizedBuffer<'_> {
282 UnsizedBuffer {
283 inner: make_ref(data),
284 max_cells: usize::MAX,
285 }
286 }
287
288 #[test]
291 fn buffer_len_and_is_empty() {
292 let mut data = vec![0i32; 4];
293 let buf = make_buffer(&mut data);
294 assert_eq!(buf.len(), 4);
295 assert!(!buf.is_empty());
296
297 let mut empty = vec![];
298 let empty_buf = make_buffer(&mut empty);
299 assert_eq!(empty_buf.len(), 0);
300 assert!(empty_buf.is_empty());
301 }
302
303 #[test]
304 fn buffer_deref_reads_values() {
305 let mut data = vec![10i32, 20, 30];
306 let buf = make_buffer(&mut data);
307 assert_eq!(&buf[..], &[10, 20, 30]);
308 assert_eq!(buf[0], 10);
309 assert_eq!(buf[2], 30);
310 }
311
312 #[test]
313 fn buffer_deref_mut_writes_values() {
314 let mut data = vec![0i32; 3];
315 let mut buf = make_buffer(&mut data);
316 buf[0] = 100;
317 buf[1] = 200;
318 buf[2] = 300;
319 assert_eq!(&data, &[100, 200, 300]);
320 }
321
322 #[test]
323 fn buffer_iter_works() {
324 let mut data = vec![1i32, 2, 3, 4];
325 let buf = make_buffer(&mut data);
326 let sum: i32 = buf.iter().sum();
327 assert_eq!(sum, 10);
328 }
329
330 #[test]
331 fn buffer_iter_mut_modifies_in_place() {
332 let mut data = vec![1i32, 2, 3];
333 let mut buf = make_buffer(&mut data);
334 buf.iter_mut().for_each(|x| *x *= 2);
335 assert_eq!(&data, &[2, 4, 6]);
336 }
337
338 #[test]
339 fn buffer_debug_format() {
340 let mut data = vec![1i32, 2, 3];
341 let buf = make_buffer(&mut data);
342 assert_eq!(format!("{buf:?}"), "[1, 2, 3]");
343 }
344
345 #[test]
346 fn buffer_as_cell_returns_amx_addr() {
347 let mut data = vec![0i32; 4];
348 let buf = make_buffer(&mut data);
349 assert_eq!(buf.as_cell(), 0);
351 }
352
353 #[test]
356 fn unsized_into_sized_normal_len() {
357 let mut data = vec![1i32, 2, 3, 4, 5];
358 let ub = make_unsized(&mut data);
359 let buf = ub.into_sized_buffer(3);
360 assert_eq!(buf.len(), 3);
361 assert_eq!(buf[0], 1);
362 assert_eq!(buf[2], 3);
363 }
364
365 #[test]
368 #[cfg_attr(
369 debug_assertions,
370 should_panic(expected = "into_sized_buffer() received len=")
371 )]
372 fn unsized_into_sized_clamps_to_max_in_release() {
373 let mut data = vec![0i32; 8];
374 let ub = make_unsized(&mut data);
375 let buf = ub.into_sized_buffer(1024 * 1024 + 1);
376 assert_eq!(buf.len(), 1024 * 1024);
378 }
379
380 #[test]
381 fn unsized_into_sized_at_exact_max() {
382 let mut data = vec![0i32; 8];
383 let ub = make_unsized(&mut data);
384 let buf = ub.into_sized_buffer(1024 * 1024);
385 assert_eq!(buf.len(), 1024 * 1024);
386 }
387
388 #[test]
389 fn into_sized_clamps_to_segment_bound() {
390 let mut data = vec![0i32; 8];
393 let ub = UnsizedBuffer {
394 inner: make_ref(&mut data),
395 max_cells: 3,
396 };
397 let buf = ub.into_sized_buffer(1000);
398 assert_eq!(buf.len(), 3);
399 }
400
401 #[test]
402 fn unsized_as_ptr_not_null() {
403 let mut data = vec![42i32];
404 let ub = make_unsized(&mut data);
405 assert!(!ub.as_ptr().is_null());
406 }
407
408 #[test]
409 fn unsized_as_cell_returns_amx_addr() {
410 let mut data = vec![0i32];
411 let ub = make_unsized(&mut data);
412 assert_eq!(ub.as_cell(), 0);
413 }
414
415 #[test]
418 fn get_as_i32_reads_value() {
419 let mut data = vec![10i32, 20, 30];
420 let buf = make_buffer(&mut data);
421 assert_eq!(buf.get_as::<i32>(0), Some(10));
422 assert_eq!(buf.get_as::<i32>(2), Some(30));
423 }
424
425 #[test]
426 fn get_as_out_of_bounds_returns_none() {
427 let mut data = vec![1i32, 2];
428 let buf = make_buffer(&mut data);
429 assert_eq!(buf.get_as::<i32>(2), None);
430 assert_eq!(buf.get_as::<i32>(99), None);
431 }
432
433 #[test]
434 fn set_as_i32_writes_value() {
435 let mut data = vec![0i32; 3];
436 let mut buf = make_buffer(&mut data);
437 assert!(buf.set_as(1, 42i32));
438 assert_eq!(data[1], 42);
439 }
440
441 #[test]
442 fn set_as_out_of_bounds_returns_false() {
443 let mut data = vec![0i32; 2];
444 let mut buf = make_buffer(&mut data);
445 assert!(!buf.set_as(5, 99i32));
446 }
447
448 #[test]
449 fn get_as_f32_roundtrip() {
450 let value = 1.5f32; let mut data = vec![value.into_cell()];
452 let buf = make_buffer(&mut data);
453 let recovered: f32 = buf.get_as::<f32>(0).unwrap();
454 assert!(
455 (recovered - value).abs() < f32::EPSILON,
456 "f32 roundtrip failed: {recovered} != {value}"
457 );
458 }
459
460 #[test]
461 fn set_as_f32_stores_bits_correctly() {
462 let mut data = vec![0i32];
463 let mut buf = make_buffer(&mut data);
464 buf.set_as(0, 1.5f32);
465 assert_eq!(data[0].cast_unsigned(), 1.5f32.to_bits());
466 }
467
468 #[test]
469 fn get_as_bool_true_and_false() {
470 let mut data = vec![1i32, 0, 42];
471 let buf = make_buffer(&mut data);
472 assert_eq!(buf.get_as::<bool>(0), Some(true));
473 assert_eq!(buf.get_as::<bool>(1), Some(false));
474 assert_eq!(buf.get_as::<bool>(2), Some(true));
476 }
477
478 #[test]
479 fn set_as_bool_writes_zero_and_one() {
480 let mut data = vec![0i32; 2];
481 let mut buf = make_buffer(&mut data);
482 buf.set_as(0, true);
483 buf.set_as(1, false);
484 assert_eq!(data[0], 1);
485 assert_eq!(data[1], 0);
486 }
487
488 #[test]
489 fn get_as_u8_reads_byte() {
490 let mut data = vec![255i32];
491 let buf = make_buffer(&mut data);
492 assert_eq!(buf.get_as::<u8>(0), Some(255u8));
493 }
494
495 #[test]
498 fn iter_as_i32_collects_all() {
499 let mut data = vec![1i32, 2, 3, 4];
500 let buf = make_buffer(&mut data);
501 let vals: Vec<i32> = buf.iter_as::<i32>().collect();
502 assert_eq!(vals, vec![1, 2, 3, 4]);
503 }
504
505 #[test]
506 fn iter_as_i32_sum() {
507 let mut data = vec![10i32, 20, 30];
508 let buf = make_buffer(&mut data);
509 let sum: i32 = buf.iter_as::<i32>().sum();
510 assert_eq!(sum, 60);
511 }
512
513 #[test]
514 fn iter_as_f32_roundtrip() {
515 let values = [1.0f32, 2.5, 1.25];
516 let mut data: Vec<i32> = values.iter().map(|&v| v.into_cell()).collect();
517 let buf = make_buffer(&mut data);
518 let recovered: Vec<f32> = buf.iter_as::<f32>().collect();
519 for (orig, got) in values.iter().zip(recovered.iter()) {
520 assert!((orig - got).abs() < f32::EPSILON, "{orig} != {got}");
521 }
522 }
523
524 #[test]
525 fn iter_as_bool_any_and_all() {
526 let mut data = vec![1i32, 0, 1, 1];
527 let buf = make_buffer(&mut data);
528 assert!(buf.iter_as::<bool>().any(|v| !v));
529 assert!(!buf.iter_as::<bool>().all(|v| v));
530 }
531
532 #[test]
533 fn iter_as_empty_buffer() {
534 let mut data: Vec<i32> = vec![];
535 let buf = make_buffer(&mut data);
536 assert_eq!(buf.iter_as::<i32>().count(), 0);
537 }
538
539 #[test]
540 fn iter_as_matches_get_as_loop() {
541 let mut data = vec![10i32, 20, 30, 40];
542 let buf = make_buffer(&mut data);
543 let via_iter: Vec<i32> = buf.iter_as::<i32>().collect();
544 let via_loop: Vec<i32> = (0..buf.len())
545 .filter_map(|i| buf.get_as::<i32>(i))
546 .collect();
547 assert_eq!(via_iter, via_loop);
548 }
549
550 #[test]
553 fn write_str_encodes_string_into_cells() {
554 let mut data = vec![0i32; 3];
556 let mut buf = make_buffer(&mut data);
557 assert!(buf.write_str("hi").is_ok());
558 assert_eq!(data[0], i32::from(b'h'));
559 assert_eq!(data[1], i32::from(b'i'));
560 assert_eq!(data[2], 0); }
562
563 #[test]
564 fn write_str_empty_string_writes_null_terminator() {
565 let mut data = vec![99i32; 2];
566 let mut buf = make_buffer(&mut data);
567 assert!(buf.write_str("").is_ok());
568 assert_eq!(data[0], 0);
569 }
570
571 #[test]
572 fn write_str_exact_fit_fails() {
573 let mut data = vec![0i32; 3];
575 let mut buf = make_buffer(&mut data);
576 assert!(buf.write_str("abc").is_err());
577 }
578
579 #[test]
582 fn unsized_write_str_sizes_and_writes() {
583 let mut data = vec![0i32; 5];
584 let ub = make_unsized(&mut data);
585 assert!(ub.write_str(5, "hi").is_ok());
586 assert_eq!(data[0], i32::from(b'h'));
587 assert_eq!(data[1], i32::from(b'i'));
588 assert_eq!(data[2], 0);
589 }
590
591 #[test]
592 fn unsized_write_str_too_long_returns_err() {
593 let mut data = vec![0i32; 3];
594 let ub = make_unsized(&mut data);
595 assert!(ub.write_str(3, "abc").is_err());
596 }
597
598 fn lcg(seed: &mut u64) -> u32 {
602 *seed = seed
603 .wrapping_mul(6_364_136_223_846_793_005)
604 .wrapping_add(1_442_695_040_888_963_407);
605 (*seed >> 33) as u32
606 }
607
608 #[test]
609 fn fuzz_get_set_as_respects_bounds() {
610 let mut seed = 0xDEAD_C0DE_1234_5678u64;
611 for _ in 0..4000 {
612 let len = (lcg(&mut seed) % 10) as usize; let mut data: Vec<i32> = vec![0; len];
614 let mut buf = make_buffer(&mut data);
615 let idx = (lcg(&mut seed) % 32) as usize; assert_eq!(buf.get_as::<i32>(idx).is_some(), idx < len);
619 let wrote = buf.set_as::<i32>(idx, 7);
621 assert_eq!(wrote, idx < len);
622 if wrote {
623 assert_eq!(buf.get_as::<i32>(idx), Some(7));
624 }
625 }
626 }
627
628 #[test]
629 fn fuzz_into_sized_never_exceeds_declared_len() {
630 let mut seed = 0x00C0_FFEE_BADD_F00Du64;
633 for _ in 0..2000 {
634 let cells = (lcg(&mut seed) % 8) as usize + 1;
635 let mut data: Vec<i32> = vec![0; cells];
636 let bound = (lcg(&mut seed) % 16) as usize;
637 let ub = UnsizedBuffer {
638 inner: make_ref(&mut data),
639 max_cells: bound,
640 };
641 let requested = (lcg(&mut seed) % 1000) as usize;
642 let buf = ub.into_sized_buffer(requested);
643 assert_eq!(buf.len(), requested.min(bound).min(1024 * 1024));
645 }
646 }
647}