Skip to main content

oxihuman_core/
patch_buffer.rs

1// Copyright (C) 2026 COOLJAPAN OU (Team KitaSan)
2// SPDX-License-Identifier: Apache-2.0
3#![allow(dead_code)]
4
5/// A buffer that accumulates byte-level patches (offset, data) for later application.
6#[allow(dead_code)]
7#[derive(Debug, Clone)]
8pub struct Patch {
9    pub offset: usize,
10    pub data: Vec<u8>,
11}
12
13#[allow(dead_code)]
14pub struct PatchBuffer {
15    patches: Vec<Patch>,
16    applied_count: u32,
17}
18
19#[allow(dead_code)]
20impl PatchBuffer {
21    pub fn new() -> Self {
22        Self {
23            patches: Vec::new(),
24            applied_count: 0,
25        }
26    }
27    pub fn add_patch(&mut self, offset: usize, data: &[u8]) {
28        self.patches.push(Patch {
29            offset,
30            data: data.to_vec(),
31        });
32    }
33    pub fn apply_to(&mut self, buf: &mut [u8]) -> u32 {
34        let mut applied = 0u32;
35        for p in &self.patches {
36            let end = (p.offset + p.data.len()).min(buf.len());
37            if p.offset < buf.len() {
38                let len = end - p.offset;
39                buf[p.offset..end].copy_from_slice(&p.data[..len]);
40                applied += 1;
41            }
42        }
43        self.applied_count += applied;
44        self.patches.clear();
45        applied
46    }
47    pub fn patch_count(&self) -> usize {
48        self.patches.len()
49    }
50    pub fn is_empty(&self) -> bool {
51        self.patches.is_empty()
52    }
53    pub fn applied_count(&self) -> u32 {
54        self.applied_count
55    }
56    pub fn total_bytes(&self) -> usize {
57        self.patches.iter().map(|p| p.data.len()).sum()
58    }
59    pub fn clear(&mut self) {
60        self.patches.clear();
61    }
62    pub fn patches(&self) -> &[Patch] {
63        &self.patches
64    }
65    pub fn max_offset(&self) -> usize {
66        self.patches
67            .iter()
68            .map(|p| p.offset + p.data.len())
69            .max()
70            .unwrap_or(0)
71    }
72}
73
74impl Default for PatchBuffer {
75    fn default() -> Self {
76        Self::new()
77    }
78}
79
80#[allow(dead_code)]
81pub fn new_patch_buffer() -> PatchBuffer {
82    PatchBuffer::new()
83}
84#[allow(dead_code)]
85pub fn pb_add(b: &mut PatchBuffer, offset: usize, data: &[u8]) {
86    b.add_patch(offset, data);
87}
88#[allow(dead_code)]
89pub fn pb_apply(b: &mut PatchBuffer, buf: &mut [u8]) -> u32 {
90    b.apply_to(buf)
91}
92#[allow(dead_code)]
93pub fn pb_count(b: &PatchBuffer) -> usize {
94    b.patch_count()
95}
96#[allow(dead_code)]
97pub fn pb_is_empty(b: &PatchBuffer) -> bool {
98    b.is_empty()
99}
100#[allow(dead_code)]
101pub fn pb_applied_count(b: &PatchBuffer) -> u32 {
102    b.applied_count()
103}
104#[allow(dead_code)]
105pub fn pb_total_bytes(b: &PatchBuffer) -> usize {
106    b.total_bytes()
107}
108#[allow(dead_code)]
109pub fn pb_clear(b: &mut PatchBuffer) {
110    b.clear();
111}
112#[allow(dead_code)]
113pub fn pb_max_offset(b: &PatchBuffer) -> usize {
114    b.max_offset()
115}
116
117#[cfg(test)]
118mod tests {
119    use super::*;
120    #[test]
121    fn test_add_apply() {
122        let mut b = new_patch_buffer();
123        pb_add(&mut b, 2, &[9, 8]);
124        let mut buf = [0u8; 8];
125        pb_apply(&mut b, &mut buf);
126        assert_eq!(buf[2], 9);
127        assert_eq!(buf[3], 8);
128    }
129    #[test]
130    fn test_clears_after_apply() {
131        let mut b = new_patch_buffer();
132        pb_add(&mut b, 0, &[1]);
133        let mut buf = [0u8; 4];
134        pb_apply(&mut b, &mut buf);
135        assert!(pb_is_empty(&b));
136    }
137    #[test]
138    fn test_applied_count() {
139        let mut b = new_patch_buffer();
140        pb_add(&mut b, 0, &[1]);
141        let mut buf = [0u8; 4];
142        pb_apply(&mut b, &mut buf);
143        assert_eq!(pb_applied_count(&b), 1);
144    }
145    #[test]
146    fn test_patch_count() {
147        let mut b = new_patch_buffer();
148        pb_add(&mut b, 0, &[1]);
149        pb_add(&mut b, 1, &[2]);
150        assert_eq!(pb_count(&b), 2);
151    }
152    #[test]
153    fn test_total_bytes() {
154        let mut b = new_patch_buffer();
155        pb_add(&mut b, 0, &[1, 2, 3]);
156        assert_eq!(pb_total_bytes(&b), 3);
157    }
158    #[test]
159    fn test_out_of_bounds_skipped() {
160        let mut b = new_patch_buffer();
161        pb_add(&mut b, 100, &[1, 2]);
162        let mut buf = [0u8; 4];
163        let n = pb_apply(&mut b, &mut buf);
164        assert_eq!(n, 0);
165    }
166    #[test]
167    fn test_clear() {
168        let mut b = new_patch_buffer();
169        pb_add(&mut b, 0, &[1]);
170        pb_clear(&mut b);
171        assert!(pb_is_empty(&b));
172    }
173    #[test]
174    fn test_max_offset() {
175        let mut b = new_patch_buffer();
176        pb_add(&mut b, 5, &[1, 2, 3]);
177        assert_eq!(pb_max_offset(&b), 8);
178    }
179    #[test]
180    fn test_empty_initially() {
181        let b = new_patch_buffer();
182        assert!(pb_is_empty(&b));
183    }
184    #[test]
185    fn test_multiple_patches() {
186        let mut b = new_patch_buffer();
187        pb_add(&mut b, 0, &[0xAA]);
188        pb_add(&mut b, 3, &[0xBB]);
189        let mut buf = [0u8; 8];
190        pb_apply(&mut b, &mut buf);
191        assert_eq!(buf[0], 0xAA);
192        assert_eq!(buf[3], 0xBB);
193    }
194}