rocketmq_common/utils/
crc32_utils.rs

1/*
2 * Licensed to the Apache Software Foundation (ASF) under one or more
3 * contributor license agreements.  See the NOTICE file distributed with
4 * this work for additional information regarding copyright ownership.
5 * The ASF licenses this file to You under the Apache License, Version 2.0
6 * (the "License"); you may not use this file except in compliance with
7 * the License.  You may obtain a copy of the License at
8 *
9 *     http://www.apache.org/licenses/LICENSE-2.0
10 *
11 * Unless required by applicable law or agreed to in writing, software
12 * distributed under the License is distributed on an "AS IS" BASIS,
13 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14 * See the License for the specific language governing permissions and
15 * limitations under the License.
16 */
17
18use bytes::Bytes;
19use crc::Crc;
20use crc::CRC_32_ISO_HDLC;
21
22/// CRC32 instance using ISO HDLC standard
23const CRC32_ALGO: Crc<u32> = Crc::<u32>::new(&CRC_32_ISO_HDLC);
24
25/// Calculate CRC32 checksum for the entire byte array.
26///
27/// Equivalent to Java's `crc32(byte[] array)`
28///
29/// # Arguments
30///
31/// * `array` - Reference to byte slice (returns 0 if empty)
32///
33/// # Returns
34///
35/// CRC32 checksum as u32 (masked with 0x7FFFFFFF to ensure positive value), or 0 if empty
36///
37/// # Examples
38///
39/// ```
40/// use rocketmq_common::utils::crc32_utils::crc32;
41///
42/// let data = b"Hello, World!";
43/// let checksum = crc32(data);
44/// assert!(checksum > 0);
45///
46/// // Empty array returns 0
47/// assert_eq!(crc32(&[]), 0);
48/// ```
49#[inline]
50pub fn crc32(array: &[u8]) -> u32 {
51    if array.is_empty() {
52        return 0;
53    }
54    crc32_range(array, 0, array.len())
55}
56
57/// Calculate CRC32 checksum for a range of bytes in the array.
58///
59/// Equivalent to Java's `crc32(byte[] array, int offset, int length)`
60///
61/// # Arguments
62///
63/// * `array` - The byte array
64/// * `offset` - Starting offset in the array
65/// * `length` - Number of bytes to process
66///
67/// # Returns
68///
69/// CRC32 checksum as u32 (masked with 0x7FFFFFFF)
70///
71/// # Panics
72///
73/// Returns 0 if offset or length are invalid (instead of panicking)
74///
75/// # Examples
76///
77/// ```
78/// use rocketmq_common::utils::crc32_utils::crc32_range;
79///
80/// let data = b"Hello, World!";
81/// let checksum = crc32_range(data, 0, 5); // Only "Hello"
82/// assert!(checksum > 0);
83/// ```
84#[inline]
85pub fn crc32_range(array: &[u8], offset: usize, length: usize) -> u32 {
86    if array.is_empty() || offset >= array.len() || offset + length > array.len() {
87        return 0;
88    }
89
90    let mut digest = CRC32_ALGO.digest();
91    digest.update(&array[offset..offset + length]);
92    digest.finalize() & 0x7FFFFFFF
93}
94
95/// Calculate CRC32 checksum for Bytes.
96///
97/// # Arguments
98///
99/// * `buf` - Optional reference to Bytes
100///
101/// # Returns
102///
103/// CRC32 checksum as u32 (masked with 0x7FFFFFFF), or 0 if None/empty
104///
105/// # Examples
106///
107/// ```
108/// use bytes::Bytes;
109/// use rocketmq_common::utils::crc32_utils::crc32_bytes;
110///
111/// let data = Bytes::from("test");
112/// let checksum = crc32_bytes(Some(&data));
113/// assert!(checksum > 0);
114/// ```
115#[inline]
116pub fn crc32_bytes(buf: Option<&Bytes>) -> u32 {
117    match buf {
118        Some(data) if !data.is_empty() => {
119            let mut digest = CRC32_ALGO.digest();
120            digest.update(data);
121            digest.finalize() & 0x7FFFFFFF
122        }
123        _ => 0,
124    }
125}
126
127/// Calculate CRC32 checksum for a range of bytes.
128///
129/// # Arguments
130///
131/// * `array` - The byte array
132/// * `offset` - Starting offset
133/// * `length` - Number of bytes to process
134///
135/// # Returns
136///
137/// CRC32 checksum as u32 (masked with 0x7FFFFFFF), or 0 if invalid range
138#[inline]
139pub fn crc32_bytes_offset(array: &[u8], offset: usize, length: usize) -> u32 {
140    crc32_range(array, offset, length)
141}
142
143/// Calculate CRC32 checksum for a byte buffer (Vec<u8>).
144///
145/// Equivalent to Java's `crc32(ByteBuffer byteBuffer)`
146///
147/// # Arguments
148///
149/// * `byte_buffer` - Reference to Vec<u8>
150///
151/// # Returns
152///
153/// CRC32 checksum as u32 (masked with 0x7FFFFFFF)
154///
155/// # Examples
156///
157/// ```
158/// use rocketmq_common::utils::crc32_utils::crc32_bytebuffer;
159///
160/// let buffer = vec![1, 2, 3, 4, 5];
161/// let checksum = crc32_bytebuffer(&buffer);
162/// assert!(checksum > 0);
163/// ```
164#[inline]
165pub fn crc32_bytebuffer(byte_buffer: &[u8]) -> u32 {
166    if byte_buffer.is_empty() {
167        return 0;
168    }
169
170    let mut digest = CRC32_ALGO.digest();
171    digest.update(byte_buffer);
172    digest.finalize() & 0x7FFFFFFF
173}
174
175/// Calculate CRC32 checksum for multiple byte buffers.
176///
177/// # Arguments
178///
179/// * `byte_buffers` - Slice of Vec<u8> buffers
180///
181/// # Returns
182///
183/// CRC32 checksum as u32 (masked with 0x7FFFFFFF)
184///
185/// # Examples
186///
187/// ```
188/// use rocketmq_common::utils::crc32_utils::crc32_bytebuffers;
189///
190/// let buffers = vec![vec![1, 2, 3], vec![4, 5]];
191/// let checksum = crc32_bytebuffers(&buffers);
192/// assert!(checksum > 0);
193/// ```
194#[inline]
195pub fn crc32_bytebuffers(byte_buffers: &[Vec<u8>]) -> u32 {
196    if byte_buffers.is_empty() {
197        return 0;
198    }
199
200    let mut digest = CRC32_ALGO.digest();
201    for buffer in byte_buffers {
202        digest.update(buffer.as_slice());
203    }
204    digest.finalize() & 0x7FFFFFFF
205}
206
207#[cfg(test)]
208mod tests {
209    use super::*;
210
211    // ========== crc32() tests ==========
212
213    #[test]
214    fn test_crc32_with_data() {
215        let buf = [1, 2, 3, 4, 5];
216        let checksum = crc32(&buf);
217        assert!(checksum > 0);
218        assert!(checksum <= 0x7FFFFFFF); // Ensure it's positive (masked)
219    }
220
221    #[test]
222    fn test_crc32_with_empty_array() {
223        let buf: &[u8] = &[];
224        assert_eq!(crc32(buf), 0);
225    }
226
227    #[test]
228    fn test_crc32_consistency() {
229        let data1 = b"Hello, World!";
230        let data2 = b"Hello, World!";
231        assert_eq!(crc32(data1), crc32(data2));
232    }
233
234    #[test]
235    fn test_crc32_different_data_different_checksum() {
236        let data1 = b"test1";
237        let data2 = b"test2";
238        assert_ne!(crc32(data1), crc32(data2));
239    }
240
241    #[test]
242    fn test_crc32_always_positive() {
243        // Test with various data to ensure result is always positive
244        let test_data = vec![
245            b"".as_slice(),
246            b"a".as_slice(),
247            b"test".as_slice(),
248            b"RocketMQ".as_slice(),
249            &[0xFF, 0xFF, 0xFF, 0xFF],
250            &[0x00, 0x01, 0x02, 0x03],
251        ];
252
253        for data in test_data {
254            if !data.is_empty() {
255                let checksum = crc32(data);
256                assert!(
257                    checksum <= 0x7FFFFFFF,
258                    "Checksum should be positive: {}",
259                    checksum
260                );
261            }
262        }
263    }
264
265    // ========== crc32_range() tests ==========
266
267    #[test]
268    fn test_crc32_range_full_array() {
269        let buf = [1, 2, 3, 4, 5];
270        let checksum1 = crc32(&buf);
271        let checksum2 = crc32_range(&buf, 0, buf.len());
272        assert_eq!(checksum1, checksum2);
273    }
274
275    #[test]
276    fn test_crc32_range_partial() {
277        let buf = b"Hello, World!";
278        let checksum = crc32_range(buf, 0, 5); // Only "Hello"
279        assert!(checksum > 0);
280    }
281
282    #[test]
283    fn test_crc32_range_middle() {
284        let buf = b"Hello, World!";
285        let checksum = crc32_range(buf, 7, 5); // Only "World"
286        assert!(checksum > 0);
287    }
288
289    #[test]
290    fn test_crc32_range_empty_array() {
291        let buf: &[u8] = &[];
292        assert_eq!(crc32_range(buf, 0, 0), 0);
293    }
294
295    #[test]
296    fn test_crc32_range_offset_out_of_bounds() {
297        let buf = [1, 2, 3, 4, 5];
298        assert_eq!(crc32_range(&buf, 10, 1), 0); // offset >= len
299    }
300
301    #[test]
302    fn test_crc32_range_length_out_of_bounds() {
303        let buf = [1, 2, 3, 4, 5];
304        assert_eq!(crc32_range(&buf, 2, 10), 0); // offset + length > len
305    }
306
307    #[test]
308    fn test_crc32_range_zero_length() {
309        let buf = [1, 2, 3, 4, 5];
310        assert_eq!(crc32_range(&buf, 0, 0), 0);
311    }
312
313    // ========== crc32_bytes() tests ==========
314
315    #[test]
316    fn test_crc32_bytes_with_data() {
317        let data = Bytes::from("test data");
318        let checksum = crc32_bytes(Some(&data));
319        assert!(checksum > 0);
320    }
321
322    #[test]
323    fn test_crc32_bytes_with_none() {
324        assert_eq!(crc32_bytes(None), 0);
325    }
326
327    #[test]
328    fn test_crc32_bytes_with_empty() {
329        let data = Bytes::new();
330        assert_eq!(crc32_bytes(Some(&data)), 0);
331    }
332
333    #[test]
334    fn test_crc32_bytes_consistency() {
335        let data1 = Bytes::from("RocketMQ");
336        let data2 = Bytes::from("RocketMQ");
337        assert_eq!(crc32_bytes(Some(&data1)), crc32_bytes(Some(&data2)));
338    }
339
340    #[test]
341    fn test_crc32_bytes_matches_slice() {
342        let data = b"test data";
343        let bytes = Bytes::from(&data[..]);
344        assert_eq!(crc32(data), crc32_bytes(Some(&bytes)));
345    }
346
347    // ========== crc32_bytes_offset() tests ==========
348
349    #[test]
350    fn test_crc32_bytes_offset_valid_range() {
351        let buf = [1, 2, 3, 4, 5];
352        let checksum = crc32_bytes_offset(&buf, 1, 3); // [2, 3, 4]
353        assert!(checksum > 0);
354    }
355
356    #[test]
357    fn test_crc32_bytes_offset_full_array() {
358        let buf = [1, 2, 3, 4, 5];
359        let checksum1 = crc32(&buf);
360        let checksum2 = crc32_bytes_offset(&buf, 0, buf.len());
361        assert_eq!(checksum1, checksum2);
362    }
363
364    #[test]
365    fn test_crc32_bytes_offset_empty_array() {
366        let buf: [u8; 0] = [];
367        assert_eq!(crc32_bytes_offset(&buf, 0, 0), 0);
368    }
369
370    #[test]
371    fn test_crc32_bytes_offset_invalid_range() {
372        let buf = [1, 2, 3, 4, 5];
373        assert_eq!(crc32_bytes_offset(&buf, 0, 10), 0);
374        assert_eq!(crc32_bytes_offset(&buf, 10, 1), 0);
375    }
376
377    // ========== crc32_bytebuffer() tests ==========
378
379    #[test]
380    fn test_crc32_bytebuffer_with_data() {
381        let buffer = vec![1, 2, 3, 4, 5];
382        let checksum = crc32_bytebuffer(&buffer);
383        assert!(checksum > 0);
384    }
385
386    #[test]
387    fn test_crc32_bytebuffer_empty() {
388        let buffer: Vec<u8> = vec![];
389        assert_eq!(crc32_bytebuffer(&buffer), 0);
390    }
391
392    #[test]
393    fn test_crc32_bytebuffer_matches_slice() {
394        let data = vec![1, 2, 3, 4, 5];
395        let checksum1 = crc32(&data);
396        let checksum2 = crc32_bytebuffer(&data);
397        assert_eq!(checksum1, checksum2);
398    }
399
400    #[test]
401    fn test_crc32_bytebuffer_large_data() {
402        let buffer = vec![0x42; 10000]; // 10KB of 'B'
403        let checksum = crc32_bytebuffer(&buffer);
404        assert!(checksum > 0);
405    }
406
407    // ========== crc32_bytebuffers() tests ==========
408
409    #[test]
410    fn test_crc32_bytebuffers_single_buffer() {
411        let buffers = vec![vec![1, 2, 3, 4, 5]];
412        let checksum = crc32_bytebuffers(&buffers);
413        assert!(checksum > 0);
414    }
415
416    #[test]
417    fn test_crc32_bytebuffers_multiple_buffers() {
418        let buffers = vec![vec![1, 2, 3], vec![4, 5]];
419        let checksum = crc32_bytebuffers(&buffers);
420        assert!(checksum > 0);
421    }
422
423    #[test]
424    fn test_crc32_bytebuffers_matches_concatenated() {
425        let buffers = vec![vec![1, 2, 3], vec![4, 5]];
426        let concatenated = vec![1, 2, 3, 4, 5];
427
428        let checksum1 = crc32_bytebuffers(&buffers);
429        let checksum2 = crc32(&concatenated);
430
431        assert_eq!(checksum1, checksum2);
432    }
433
434    #[test]
435    fn test_crc32_bytebuffers_empty() {
436        let buffers: Vec<Vec<u8>> = vec![];
437        assert_eq!(crc32_bytebuffers(&buffers), 0);
438    }
439
440    #[test]
441    fn test_crc32_bytebuffers_with_empty_buffers() {
442        let buffers = vec![vec![], vec![1, 2, 3], vec![]];
443        let checksum = crc32_bytebuffers(&buffers);
444        assert!(checksum > 0);
445    }
446
447    #[test]
448    fn test_crc32_bytebuffers_many_small_buffers() {
449        let buffers = vec![vec![1], vec![2], vec![3], vec![4], vec![5]];
450        let concatenated = vec![1, 2, 3, 4, 5];
451
452        assert_eq!(crc32_bytebuffers(&buffers), crc32(&concatenated));
453    }
454
455    // ========== Integration and edge cases ==========
456
457    #[test]
458    fn test_all_functions_return_same_for_same_data() {
459        let data = b"RocketMQ";
460        let bytes = Bytes::from(&data[..]);
461        let buffer = data.to_vec();
462        let buffers = vec![data.to_vec()];
463
464        let c1 = crc32(data);
465        let c2 = crc32_range(data, 0, data.len());
466        let c3 = crc32_bytes(Some(&bytes));
467        let c4 = crc32_bytes_offset(data, 0, data.len());
468        let c5 = crc32_bytebuffer(&buffer);
469        let c6 = crc32_bytebuffers(&buffers);
470
471        assert_eq!(c1, c2);
472        assert_eq!(c1, c3);
473        assert_eq!(c1, c4);
474        assert_eq!(c1, c5);
475        assert_eq!(c1, c6);
476    }
477
478    #[test]
479    fn test_crc32_with_special_characters() {
480        let data = b"!@#$%^&*()_+-=[]{}|;':\",./<>?";
481        let checksum = crc32(data);
482        assert!(checksum > 0);
483    }
484
485    #[test]
486    fn test_crc32_with_unicode_bytes() {
487        let data = "你好,世界!".as_bytes();
488        let checksum = crc32(data);
489        assert!(checksum > 0);
490    }
491
492    #[test]
493    fn test_crc32_with_binary_data() {
494        let data = [0x00, 0xFF, 0xAA, 0x55, 0x12, 0x34, 0x56, 0x78];
495        let checksum = crc32(&data);
496        assert!(checksum > 0);
497    }
498}