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 let len = len.min(self.max_cells).min(MAX_BUFFER_CELLS);
192 Buffer::new(self.inner, len)
193 }
194
195 #[inline]
197 #[must_use]
198 pub fn as_ptr(&self) -> *const i32 {
199 self.inner.as_ptr()
200 }
201
202 #[inline]
204 pub fn as_mut_ptr(&mut self) -> *mut i32 {
205 self.inner.as_mut_ptr()
206 }
207
208 pub(crate) fn max_cells(&self) -> usize {
211 self.max_cells
212 }
213
214 #[doc(hidden)]
216 #[must_use]
217 pub fn from_raw_parts(inner: Ref<'amx, i32>) -> Self {
218 UnsizedBuffer {
219 inner,
220 max_cells: usize::MAX,
221 }
222 }
223
224 pub fn write_str(self, max_len: usize, s: &str) -> AmxResult<()> {
233 let mut buf = self.into_sized_buffer(max_len);
234 string::put_in_buffer(&mut buf, s)
235 }
236
237 pub fn write_str_checked(self, max_len: usize, s: &str) -> AmxResult<bool> {
246 let mut buf = self.into_sized_buffer(max_len);
247 string::put_in_buffer_checked(&mut buf, s)
248 }
249}
250
251impl<'amx> AmxCell<'amx> for UnsizedBuffer<'amx> {
252 fn from_raw(amx: &'amx Amx, cell: i32) -> AmxResult<UnsizedBuffer<'amx>> {
253 let inner = amx.get_ref(cell)?;
254 let max_cells = amx.stp().map_or(usize::MAX, |stp| {
258 usize::try_from((stp - cell).max(0) / 4).unwrap_or(0)
259 });
260 Ok(UnsizedBuffer { inner, max_cells })
261 }
262
263 #[inline]
264 fn as_cell(&self) -> i32 {
265 self.inner.as_cell()
266 }
267}
268
269#[cfg(test)]
270mod tests {
271 use super::*;
272 use crate::cell::Ref;
273 use crate::cell::repr::CellConvert;
274
275 fn make_ref(data: &mut Vec<i32>) -> Ref<'_, i32> {
276 unsafe { Ref::new(0, data.as_mut_ptr()) }
277 }
278
279 fn make_buffer(data: &mut Vec<i32>) -> Buffer<'_> {
280 let len = data.len();
281 let r = make_ref(data);
282 Buffer::new(r, len)
283 }
284
285 fn make_unsized(data: &mut Vec<i32>) -> UnsizedBuffer<'_> {
286 UnsizedBuffer {
287 inner: make_ref(data),
288 max_cells: usize::MAX,
289 }
290 }
291
292 #[test]
295 fn buffer_len_and_is_empty() {
296 let mut data = vec![0i32; 4];
297 let buf = make_buffer(&mut data);
298 assert_eq!(buf.len(), 4);
299 assert!(!buf.is_empty());
300
301 let mut empty = vec![];
302 let empty_buf = make_buffer(&mut empty);
303 assert_eq!(empty_buf.len(), 0);
304 assert!(empty_buf.is_empty());
305 }
306
307 #[test]
308 fn buffer_deref_reads_values() {
309 let mut data = vec![10i32, 20, 30];
310 let buf = make_buffer(&mut data);
311 assert_eq!(&buf[..], &[10, 20, 30]);
312 assert_eq!(buf[0], 10);
313 assert_eq!(buf[2], 30);
314 }
315
316 #[test]
317 fn buffer_deref_mut_writes_values() {
318 let mut data = vec![0i32; 3];
319 let mut buf = make_buffer(&mut data);
320 buf[0] = 100;
321 buf[1] = 200;
322 buf[2] = 300;
323 assert_eq!(&data, &[100, 200, 300]);
324 }
325
326 #[test]
327 fn buffer_iter_works() {
328 let mut data = vec![1i32, 2, 3, 4];
329 let buf = make_buffer(&mut data);
330 let sum: i32 = buf.iter().sum();
331 assert_eq!(sum, 10);
332 }
333
334 #[test]
335 fn buffer_iter_mut_modifies_in_place() {
336 let mut data = vec![1i32, 2, 3];
337 let mut buf = make_buffer(&mut data);
338 buf.iter_mut().for_each(|x| *x *= 2);
339 assert_eq!(&data, &[2, 4, 6]);
340 }
341
342 #[test]
343 fn buffer_debug_format() {
344 let mut data = vec![1i32, 2, 3];
345 let buf = make_buffer(&mut data);
346 assert_eq!(format!("{buf:?}"), "[1, 2, 3]");
347 }
348
349 #[test]
350 fn buffer_as_cell_returns_amx_addr() {
351 let mut data = vec![0i32; 4];
352 let buf = make_buffer(&mut data);
353 assert_eq!(buf.as_cell(), 0);
355 }
356
357 #[test]
360 fn unsized_into_sized_normal_len() {
361 let mut data = vec![1i32, 2, 3, 4, 5];
362 let ub = make_unsized(&mut data);
363 let buf = ub.into_sized_buffer(3);
364 assert_eq!(buf.len(), 3);
365 assert_eq!(buf[0], 1);
366 assert_eq!(buf[2], 3);
367 }
368
369 #[test]
372 fn unsized_into_sized_clamps_to_max() {
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);
377 }
378
379 #[test]
380 fn unsized_into_sized_at_exact_max() {
381 let mut data = vec![0i32; 8];
382 let ub = make_unsized(&mut data);
383 let buf = ub.into_sized_buffer(1024 * 1024);
384 assert_eq!(buf.len(), 1024 * 1024);
385 }
386
387 #[test]
388 fn into_sized_clamps_to_segment_bound() {
389 let mut data = vec![0i32; 8];
392 let ub = UnsizedBuffer {
393 inner: make_ref(&mut data),
394 max_cells: 3,
395 };
396 let buf = ub.into_sized_buffer(1000);
397 assert_eq!(buf.len(), 3);
398 }
399
400 #[test]
401 fn unsized_as_ptr_not_null() {
402 let mut data = vec![42i32];
403 let ub = make_unsized(&mut data);
404 assert!(!ub.as_ptr().is_null());
405 }
406
407 #[test]
408 fn unsized_as_cell_returns_amx_addr() {
409 let mut data = vec![0i32];
410 let ub = make_unsized(&mut data);
411 assert_eq!(ub.as_cell(), 0);
412 }
413
414 #[test]
417 fn get_as_i32_reads_value() {
418 let mut data = vec![10i32, 20, 30];
419 let buf = make_buffer(&mut data);
420 assert_eq!(buf.get_as::<i32>(0), Some(10));
421 assert_eq!(buf.get_as::<i32>(2), Some(30));
422 }
423
424 #[test]
425 fn get_as_out_of_bounds_returns_none() {
426 let mut data = vec![1i32, 2];
427 let buf = make_buffer(&mut data);
428 assert_eq!(buf.get_as::<i32>(2), None);
429 assert_eq!(buf.get_as::<i32>(99), None);
430 }
431
432 #[test]
433 fn set_as_i32_writes_value() {
434 let mut data = vec![0i32; 3];
435 let mut buf = make_buffer(&mut data);
436 assert!(buf.set_as(1, 42i32));
437 assert_eq!(data[1], 42);
438 }
439
440 #[test]
441 fn set_as_out_of_bounds_returns_false() {
442 let mut data = vec![0i32; 2];
443 let mut buf = make_buffer(&mut data);
444 assert!(!buf.set_as(5, 99i32));
445 }
446
447 #[test]
448 fn get_as_f32_roundtrip() {
449 let value = 1.5f32; let mut data = vec![value.into_cell()];
451 let buf = make_buffer(&mut data);
452 let recovered: f32 = buf.get_as::<f32>(0).unwrap();
453 assert!(
454 (recovered - value).abs() < f32::EPSILON,
455 "f32 roundtrip failed: {recovered} != {value}"
456 );
457 }
458
459 #[test]
460 fn set_as_f32_stores_bits_correctly() {
461 let mut data = vec![0i32];
462 let mut buf = make_buffer(&mut data);
463 buf.set_as(0, 1.5f32);
464 assert_eq!(data[0].cast_unsigned(), 1.5f32.to_bits());
465 }
466
467 #[test]
468 fn get_as_bool_true_and_false() {
469 let mut data = vec![1i32, 0, 42];
470 let buf = make_buffer(&mut data);
471 assert_eq!(buf.get_as::<bool>(0), Some(true));
472 assert_eq!(buf.get_as::<bool>(1), Some(false));
473 assert_eq!(buf.get_as::<bool>(2), Some(true));
475 }
476
477 #[test]
478 fn set_as_bool_writes_zero_and_one() {
479 let mut data = vec![0i32; 2];
480 let mut buf = make_buffer(&mut data);
481 buf.set_as(0, true);
482 buf.set_as(1, false);
483 assert_eq!(data[0], 1);
484 assert_eq!(data[1], 0);
485 }
486
487 #[test]
488 fn get_as_u8_reads_byte() {
489 let mut data = vec![255i32];
490 let buf = make_buffer(&mut data);
491 assert_eq!(buf.get_as::<u8>(0), Some(255u8));
492 }
493
494 #[test]
497 fn iter_as_i32_collects_all() {
498 let mut data = vec![1i32, 2, 3, 4];
499 let buf = make_buffer(&mut data);
500 let vals: Vec<i32> = buf.iter_as::<i32>().collect();
501 assert_eq!(vals, vec![1, 2, 3, 4]);
502 }
503
504 #[test]
505 fn iter_as_i32_sum() {
506 let mut data = vec![10i32, 20, 30];
507 let buf = make_buffer(&mut data);
508 let sum: i32 = buf.iter_as::<i32>().sum();
509 assert_eq!(sum, 60);
510 }
511
512 #[test]
513 fn iter_as_f32_roundtrip() {
514 let values = [1.0f32, 2.5, 1.25];
515 let mut data: Vec<i32> = values.iter().map(|&v| v.into_cell()).collect();
516 let buf = make_buffer(&mut data);
517 let recovered: Vec<f32> = buf.iter_as::<f32>().collect();
518 for (orig, got) in values.iter().zip(recovered.iter()) {
519 assert!((orig - got).abs() < f32::EPSILON, "{orig} != {got}");
520 }
521 }
522
523 #[test]
524 fn iter_as_bool_any_and_all() {
525 let mut data = vec![1i32, 0, 1, 1];
526 let buf = make_buffer(&mut data);
527 assert!(buf.iter_as::<bool>().any(|v| !v));
528 assert!(!buf.iter_as::<bool>().all(|v| v));
529 }
530
531 #[test]
532 fn iter_as_empty_buffer() {
533 let mut data: Vec<i32> = vec![];
534 let buf = make_buffer(&mut data);
535 assert_eq!(buf.iter_as::<i32>().count(), 0);
536 }
537
538 #[test]
539 fn iter_as_matches_get_as_loop() {
540 let mut data = vec![10i32, 20, 30, 40];
541 let buf = make_buffer(&mut data);
542 let via_iter: Vec<i32> = buf.iter_as::<i32>().collect();
543 let via_loop: Vec<i32> = (0..buf.len())
544 .filter_map(|i| buf.get_as::<i32>(i))
545 .collect();
546 assert_eq!(via_iter, via_loop);
547 }
548
549 #[test]
552 fn write_str_encodes_string_into_cells() {
553 let mut data = vec![0i32; 3];
555 let mut buf = make_buffer(&mut data);
556 assert!(buf.write_str("hi").is_ok());
557 assert_eq!(data[0], i32::from(b'h'));
558 assert_eq!(data[1], i32::from(b'i'));
559 assert_eq!(data[2], 0); }
561
562 #[test]
563 fn write_str_empty_string_writes_null_terminator() {
564 let mut data = vec![99i32; 2];
565 let mut buf = make_buffer(&mut data);
566 assert!(buf.write_str("").is_ok());
567 assert_eq!(data[0], 0);
568 }
569
570 #[test]
571 fn write_str_exact_fit_fails() {
572 let mut data = vec![0i32; 3];
574 let mut buf = make_buffer(&mut data);
575 assert!(buf.write_str("abc").is_err());
576 }
577
578 #[test]
581 fn unsized_write_str_sizes_and_writes() {
582 let mut data = vec![0i32; 5];
583 let ub = make_unsized(&mut data);
584 assert!(ub.write_str(5, "hi").is_ok());
585 assert_eq!(data[0], i32::from(b'h'));
586 assert_eq!(data[1], i32::from(b'i'));
587 assert_eq!(data[2], 0);
588 }
589
590 #[test]
591 fn unsized_write_str_too_long_returns_err() {
592 let mut data = vec![0i32; 3];
593 let ub = make_unsized(&mut data);
594 assert!(ub.write_str(3, "abc").is_err());
595 }
596
597 fn lcg(seed: &mut u64) -> u32 {
601 *seed = seed
602 .wrapping_mul(6_364_136_223_846_793_005)
603 .wrapping_add(1_442_695_040_888_963_407);
604 (*seed >> 33) as u32
605 }
606
607 #[test]
608 fn fuzz_get_set_as_respects_bounds() {
609 let mut seed = 0xDEAD_C0DE_1234_5678u64;
610 for _ in 0..4000 {
611 let len = (lcg(&mut seed) % 10) as usize; let mut data: Vec<i32> = vec![0; len];
613 let mut buf = make_buffer(&mut data);
614 let idx = (lcg(&mut seed) % 32) as usize; assert_eq!(buf.get_as::<i32>(idx).is_some(), idx < len);
618 let wrote = buf.set_as::<i32>(idx, 7);
620 assert_eq!(wrote, idx < len);
621 if wrote {
622 assert_eq!(buf.get_as::<i32>(idx), Some(7));
623 }
624 }
625 }
626
627 #[test]
628 fn fuzz_into_sized_never_exceeds_declared_len() {
629 let mut seed = 0x00C0_FFEE_BADD_F00Du64;
632 for _ in 0..2000 {
633 let cells = (lcg(&mut seed) % 8) as usize + 1;
634 let mut data: Vec<i32> = vec![0; cells];
635 let bound = (lcg(&mut seed) % 16) as usize;
636 let ub = UnsizedBuffer {
637 inner: make_ref(&mut data),
638 max_cells: bound,
639 };
640 let requested = (lcg(&mut seed) % 1000) as usize;
641 let buf = ub.into_sized_buffer(requested);
642 assert_eq!(buf.len(), requested.min(bound).min(1024 * 1024));
644 }
645 }
646}