Skip to main content

rocketmq_model/utils/
message_utils.rs

1// Copyright 2023 The RocketMQ Rust Authors
2//
3// Licensed under the Apache License, Version 2.0 (the "License");
4// you may not use this file except in compliance with the License.
5// You may obtain a copy of the License at
6//
7//     http://www.apache.org/licenses/LICENSE-2.0
8//
9// Unless required by applicable law or agreed to in writing, software
10// distributed under the License is distributed on an "AS IS" BASIS,
11// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12// See the License for the specific language governing permissions and
13// limitations under the License.
14
15use std::collections::HashSet;
16use std::net::IpAddr;
17use std::net::Ipv4Addr;
18use std::net::Ipv6Addr;
19use std::net::SocketAddr;
20
21use bytes::BufMut;
22use cheetah_string::CheetahFinder;
23use cheetah_string::CheetahString;
24
25use crate::codec::bytes_to_string;
26use crate::codec::string_to_bytes;
27use crate::codec::NAME_VALUE_SEPARATOR;
28use crate::codec::PROPERTY_SEPARATOR;
29use crate::common::message::message_ext::MessageExt;
30use crate::common::message::MessageConst;
31
32pub fn get_sharding_key_index(sharding_key: &str, index_size: usize) -> usize {
33    if index_size == 0 {
34        return 0;
35    }
36
37    let hash = murmur3_32_java_hash(sharding_key.as_bytes()) as i64;
38    (hash % index_size as i64).unsigned_abs() as usize
39}
40
41fn murmur3_32_java_hash(bytes: &[u8]) -> i32 {
42    const C1: u32 = 0xcc9e2d51;
43    const C2: u32 = 0x1b873593;
44
45    let mut h1 = 0u32;
46    let block_count = bytes.len() / 4;
47
48    for block_index in 0..block_count {
49        let offset = block_index * 4;
50        let mut k1 = u32::from_le_bytes([bytes[offset], bytes[offset + 1], bytes[offset + 2], bytes[offset + 3]]);
51
52        k1 = k1.wrapping_mul(C1);
53        k1 = k1.rotate_left(15);
54        k1 = k1.wrapping_mul(C2);
55
56        h1 ^= k1;
57        h1 = h1.rotate_left(13);
58        h1 = h1.wrapping_mul(5).wrapping_add(0xe6546b64);
59    }
60
61    let tail = &bytes[block_count * 4..];
62    let mut k1 = 0u32;
63    match tail.len() {
64        3 => {
65            k1 ^= (tail[2] as u32) << 16;
66            k1 ^= (tail[1] as u32) << 8;
67            k1 ^= tail[0] as u32;
68        }
69        2 => {
70            k1 ^= (tail[1] as u32) << 8;
71            k1 ^= tail[0] as u32;
72        }
73        1 => {
74            k1 ^= tail[0] as u32;
75        }
76        _ => {}
77    }
78
79    if !tail.is_empty() {
80        k1 = k1.wrapping_mul(C1);
81        k1 = k1.rotate_left(15);
82        k1 = k1.wrapping_mul(C2);
83        h1 ^= k1;
84    }
85
86    h1 ^= bytes.len() as u32;
87    h1 ^= h1 >> 16;
88    h1 = h1.wrapping_mul(0x85ebca6b);
89    h1 ^= h1 >> 13;
90    h1 = h1.wrapping_mul(0xc2b2ae35);
91    h1 ^= h1 >> 16;
92
93    h1 as i32
94}
95
96pub fn get_sharding_key_index_by_msg(msg: &MessageExt, index_size: usize) -> usize {
97    let sharding_key = msg
98        .message
99        .properties()
100        .as_map()
101        .get(MessageConst::PROPERTY_SHARDING_KEY)
102        .cloned()
103        .unwrap_or_default();
104
105    get_sharding_key_index(sharding_key.as_str(), index_size)
106}
107
108pub fn get_sharding_key_indexes(msgs: &[MessageExt], index_size: usize) -> HashSet<usize> {
109    let mut index_set = HashSet::with_capacity(index_size);
110    for msg in msgs {
111        let idx = get_sharding_key_index_by_msg(msg, index_size);
112        index_set.insert(idx);
113    }
114    index_set
115}
116
117fn delete_property_if_present(properties_string: &str, name: &str) -> Option<String> {
118    if properties_string.is_empty() || name.is_empty() {
119        return None;
120    }
121
122    fn find_from(finder: &CheetahFinder<'_>, haystack: &str, start: usize) -> Option<usize> {
123        haystack
124            .get(start..)
125            .and_then(|remaining| finder.find_in(remaining).map(|offset| start + offset))
126    }
127
128    fn find_property_name_from(
129        finder: &CheetahFinder<'_>,
130        haystack: &str,
131        bytes: &[u8],
132        name_len: usize,
133        start: usize,
134    ) -> Option<usize> {
135        let property_separator = PROPERTY_SEPARATOR as u8;
136        let name_value_separator = NAME_VALUE_SEPARATOR as u8;
137        let mut start_idx = start;
138
139        loop {
140            let candidate_idx = find_from(finder, haystack, start_idx)?;
141            start_idx = candidate_idx + name_len;
142
143            let before_ok = candidate_idx == 0 || bytes.get(candidate_idx - 1) == Some(&property_separator);
144            let after_idx = candidate_idx + name_len;
145            let after_ok = bytes.get(after_idx) == Some(&name_value_separator);
146            if before_ok && after_ok {
147                return Some(candidate_idx);
148            }
149        }
150    }
151
152    let finder = CheetahFinder::new(name);
153    let bytes = properties_string.as_bytes();
154    let mut next_property_idx = find_property_name_from(&finder, properties_string, bytes, name.len(), 0)?;
155
156    let mut idx0 = 0;
157    let mut string_builder = String::with_capacity(properties_string.len());
158
159    loop {
160        // there are characters that need to be cropped
161        string_builder.push_str(&properties_string[idx0..next_property_idx]);
162        // move idx2 to the end of the cropped character
163        let value_start = next_property_idx + name.len() + 1;
164        match properties_string
165            .get(value_start..)
166            .and_then(|rest| rest.find(PROPERTY_SEPARATOR))
167            .map(|offset| value_start + offset)
168        {
169            Some(idx2) => {
170                idx0 = idx2 + 1;
171                let Some(idx1) = find_property_name_from(&finder, properties_string, bytes, name.len(), idx0) else {
172                    // there are no characters that need to be skipped. Append all remaining characters.
173                    string_builder.push_str(&properties_string[idx0..]);
174                    break;
175                };
176                next_property_idx = idx1;
177            }
178            None => {
179                break;
180            }
181        }
182    }
183
184    Some(string_builder)
185}
186
187pub fn delete_property(properties_string: &str, name: &str) -> String {
188    delete_property_if_present(properties_string, name).unwrap_or_else(|| properties_string.to_string())
189}
190
191pub fn delete_property_v2(properties_str: &str, name: &str) -> String {
192    delete_property(properties_str, name)
193}
194
195pub fn delete_property_to_cheetah_string(properties_string: &CheetahString, name: &str) -> CheetahString {
196    delete_property_if_present(properties_string.as_str(), name)
197        .map(CheetahString::from_string)
198        .unwrap_or_else(|| properties_string.clone())
199}
200
201pub fn build_message_id(socket_addr: SocketAddr, wrote_offset: i64) -> String {
202    let mut msg_id_vec = match socket_addr {
203        SocketAddr::V4(addr) => {
204            let mut msg_id_vec = Vec::<u8>::with_capacity(16);
205            msg_id_vec.extend_from_slice(&addr.ip().octets());
206            msg_id_vec.put_i32(addr.port() as i32);
207            msg_id_vec
208        }
209        SocketAddr::V6(addr) => {
210            let mut msg_id_vec = Vec::<u8>::with_capacity(28);
211            msg_id_vec.extend_from_slice(&addr.ip().octets());
212            msg_id_vec.put_i32(addr.port() as i32);
213            msg_id_vec
214        }
215    };
216    msg_id_vec.put_i64(wrote_offset);
217    bytes_to_string(msg_id_vec.as_slice())
218}
219
220pub fn socket_address_to_vec(socket_addr: SocketAddr) -> Vec<u8> {
221    match socket_addr {
222        SocketAddr::V4(addr) => {
223            let mut msg_id_vec = Vec::<u8>::with_capacity(8);
224            msg_id_vec.extend_from_slice(&addr.ip().octets());
225            msg_id_vec.put_i32(addr.port() as i32);
226            msg_id_vec
227        }
228        SocketAddr::V6(addr) => {
229            let mut msg_id_vec = Vec::<u8>::with_capacity(20);
230            msg_id_vec.extend_from_slice(&addr.ip().octets());
231            msg_id_vec.put_i32(addr.port() as i32);
232            msg_id_vec
233        }
234    }
235}
236
237pub fn build_batch_message_id(
238    socket_addr: SocketAddr,
239    store_host_length: i32,
240    batch_size: usize,
241    phy_pos: &[i64],
242) -> String {
243    if batch_size == 0 {
244        return String::new();
245    }
246    let msg_id_len = (store_host_length + 8) as usize;
247    let mut msg_id_vec = vec![0u8; msg_id_len];
248    msg_id_vec[..store_host_length as usize].copy_from_slice(&socket_address_to_vec(socket_addr));
249    let mut message_id = String::with_capacity(batch_size * msg_id_len * 2 + batch_size - 1);
250    for (index, value) in phy_pos.iter().enumerate() {
251        msg_id_vec[msg_id_len - 8..msg_id_len].copy_from_slice(&value.to_be_bytes());
252        if index != 0 {
253            message_id.push(',');
254        }
255        message_id.push_str(&bytes_to_string(&msg_id_vec));
256    }
257    message_id
258}
259
260pub fn parse_message_id(msg_id: impl Into<String>) -> (SocketAddr, i64) {
261    fn invalid_message_id() -> (SocketAddr, i64) {
262        (SocketAddr::new(IpAddr::V4(Ipv4Addr::UNSPECIFIED), 0), -1)
263    }
264
265    let msg_id = msg_id.into();
266    let Some(bytes) = string_to_bytes(&msg_id) else {
267        return invalid_message_id();
268    };
269
270    let ip_len = match bytes.len() {
271        16 => 4,
272        28 => 16,
273        _ => return invalid_message_id(),
274    };
275
276    let port_start = ip_len;
277    let offset_start = port_start + 4;
278    let Some(port_bytes) = bytes.get(port_start..offset_start) else {
279        return invalid_message_id();
280    };
281    let Some(offset_bytes) = bytes.get(offset_start..offset_start + 8) else {
282        return invalid_message_id();
283    };
284
285    let port = i32::from_be_bytes([port_bytes[0], port_bytes[1], port_bytes[2], port_bytes[3]]);
286    if !(0..=u16::MAX as i32).contains(&port) {
287        return invalid_message_id();
288    }
289
290    let addr = if ip_len == 4 {
291        SocketAddr::new(
292            IpAddr::V4(Ipv4Addr::new(bytes[0], bytes[1], bytes[2], bytes[3])),
293            port as u16,
294        )
295    } else {
296        let mut ip = [0u8; 16];
297        ip.copy_from_slice(&bytes[..16]);
298        SocketAddr::new(IpAddr::V6(Ipv6Addr::from(ip)), port as u16)
299    };
300
301    let offset = i64::from_be_bytes([
302        offset_bytes[0],
303        offset_bytes[1],
304        offset_bytes[2],
305        offset_bytes[3],
306        offset_bytes[4],
307        offset_bytes[5],
308        offset_bytes[6],
309        offset_bytes[7],
310    ]);
311
312    (addr, offset)
313}
314
315#[cfg(test)]
316mod tests {
317    use std::net::Ipv4Addr;
318
319    use super::*;
320    use crate::common::message::message_ext::MessageExt;
321
322    #[test]
323    fn test_get_sharding_key_index() {
324        let sharding_key = "example_key";
325        let index_size = 10;
326        let result = get_sharding_key_index(sharding_key, index_size);
327        assert!(result < index_size);
328    }
329
330    #[test]
331    fn murmur3_32_java_hash_matches_guava_hash_bytes() {
332        let cases = [
333            ("", 0),
334            ("example_key", 1_493_934_834),
335            ("key1", -1_684_602_587),
336            ("key2", 1_936_800_180),
337            ("order-123", -1_381_100_355),
338            ("\u{4e2d}\u{6587}key", 1_259_819_139),
339            ("rocketmq", -184_929_330),
340            ("sharding-999", 467_656_710),
341        ];
342
343        for (key, expected_hash) in cases {
344            assert_eq!(murmur3_32_java_hash(key.as_bytes()), expected_hash, "key={key}");
345        }
346    }
347
348    #[test]
349    fn get_sharding_key_index_matches_java_remainder_semantics() {
350        let cases = [
351            ("example_key", 10, 4),
352            ("example_key", 32, 18),
353            ("example_key", 1024, 754),
354            ("key1", 10, 7),
355            ("key1", 16, 11),
356            ("key1", 1024, 731),
357            ("key2", 10, 0),
358            ("order-123", 10, 5),
359            ("\u{4e2d}\u{6587}key", 10, 9),
360            ("\u{4e2d}\u{6587}key", 1024, 131),
361            ("rocketmq", 1024, 50),
362            ("sharding-999", 1024, 6),
363        ];
364
365        for (key, index_size, expected_index) in cases {
366            assert_eq!(
367                get_sharding_key_index(key, index_size),
368                expected_index,
369                "key={key}, index_size={index_size}"
370            );
371        }
372    }
373
374    #[test]
375    fn get_sharding_key_index_zero_size_returns_zero() {
376        assert_eq!(get_sharding_key_index("key1", 0), 0);
377    }
378
379    #[test]
380    fn test_get_sharding_key_index_by_msg() {
381        let mut message = MessageExt::default();
382        message
383            .message
384            .properties_mut()
385            .as_map_mut()
386            .insert(MessageConst::PROPERTY_SHARDING_KEY.into(), "example_key".into());
387        let index_size = 10;
388        let result = get_sharding_key_index_by_msg(&message, index_size);
389        assert!(result < index_size);
390    }
391
392    #[test]
393    fn test_get_sharding_key_indexes() {
394        let mut messages = Vec::new();
395        let mut message1 = MessageExt::default();
396        message1
397            .message
398            .properties_mut()
399            .as_map_mut()
400            .insert(MessageConst::PROPERTY_SHARDING_KEY.into(), "key1".into());
401        messages.push(message1);
402        let mut message2 = MessageExt::default();
403        message2
404            .message
405            .properties_mut()
406            .as_map_mut()
407            .insert(MessageConst::PROPERTY_SHARDING_KEY.into(), "key2".into());
408        messages.push(message2);
409        let index_size = 10;
410        let result = get_sharding_key_indexes(&messages, index_size);
411        assert_eq!(result.len(), 2);
412    }
413
414    #[test]
415    fn test_delete_property() {
416        let properties_string = "aa\u{0001}bb\u{0002}cc\u{0001}bb\u{0002}";
417        let name = "aa";
418        let result = delete_property(properties_string, name);
419        assert_eq!(result, "cc\u{0001}bb\u{0002}");
420
421        let result = delete_property_v2(properties_string, name);
422        assert_eq!(result, "cc\u{0001}bb\u{0002}");
423    }
424
425    #[test]
426    fn delete_property_removes_property_correctly() {
427        let properties_string = "key1\u{0001}value1\u{0002}key2\u{0001}value2\u{0002}key3\u{0001}value3";
428        let name = "key2";
429        let result = delete_property(properties_string, name);
430        assert_eq!(result, "key1\u{0001}value1\u{0002}key3\u{0001}value3");
431
432        let result = delete_property_v2(properties_string, name);
433        assert_eq!(result, "key1\u{0001}value1\u{0002}key3\u{0001}value3");
434    }
435
436    #[test]
437    fn delete_property_handles_empty_string() {
438        let properties_string = "";
439        let name = "key";
440        let result = delete_property(properties_string, name);
441        assert_eq!(result, "");
442    }
443
444    #[test]
445    fn delete_property_handles_non_existent_key() {
446        let properties_string = "key1\u{0001}value1\u{0002}key2\u{0001}value2";
447        let name = "key3";
448        let result = delete_property(properties_string, name);
449        assert_eq!(result, "key1\u{0001}value1\u{0002}key2\u{0001}value2");
450    }
451
452    #[test]
453    fn delete_property_handles_key_at_start() {
454        let properties_string = "key1\u{0001}value1\u{0002}key2\u{0001}value2";
455        let name = "key1";
456        let result = delete_property(properties_string, name);
457        assert_eq!(result, "key2\u{0001}value2");
458    }
459
460    #[test]
461    fn delete_property_handles_key_at_end() {
462        let properties_string = "key1\u{0001}value1\u{0002}key2\u{0001}value2";
463        let name = "key2";
464        let result = delete_property(properties_string, name);
465        assert_eq!(result, "key1\u{0001}value1\u{0002}");
466    }
467
468    #[test]
469    fn delete_property_handles_multiple_occurrences() {
470        let properties_string = "key1\u{0001}value1\u{0002}key2\u{0001}value2\u{0002}key1\u{0001}value3";
471        let name = "key1";
472        let result = delete_property(properties_string, name);
473        assert_eq!(result, "key2\u{0001}value2\u{0002}");
474    }
475
476    #[test]
477    fn delete_property_handles_single_character_key() {
478        let properties_string = "a\u{0001}alpha\u{0002}ba\u{0001}keep";
479
480        assert_eq!(delete_property(properties_string, "a"), "ba\u{0001}keep");
481        assert_eq!(delete_property_v2(properties_string, "a"), "ba\u{0001}keep");
482    }
483
484    #[test]
485    fn delete_property_handles_multi_character_key_boundaries() {
486        let properties_string = "Order\u{0001}remove\u{0002}OrderId\u{0001}keep";
487
488        assert_eq!(delete_property(properties_string, "Order"), "OrderId\u{0001}keep");
489        assert_eq!(delete_property_v2(properties_string, "Order"), "OrderId\u{0001}keep");
490    }
491
492    #[test]
493    fn delete_property_preserves_input_for_empty_name() {
494        let properties_string = "key\u{0001}value";
495
496        assert_eq!(delete_property(properties_string, ""), properties_string);
497        assert_eq!(delete_property_v2(properties_string, ""), properties_string);
498    }
499
500    #[test]
501    fn delete_property_preserves_unicode_value_boundaries() {
502        let properties_string = "region\u{0001}\u{534e}\u{4e1c}\u{0002}tier\u{0001}gold";
503
504        assert_eq!(
505            delete_property(properties_string, "tier"),
506            "region\u{0001}\u{534e}\u{4e1c}\u{0002}"
507        );
508        assert_eq!(
509            delete_property_v2(properties_string, "tier"),
510            "region\u{0001}\u{534e}\u{4e1c}\u{0002}"
511        );
512    }
513
514    #[test]
515    fn delete_property_matches_java_edge_cases() {
516        let cases = [
517            ("", "KeyA", ""),
518            ("KeyA\u{0001}ValueA", "KeyA", ""),
519            ("KeyA\u{0001}ValueA\u{0002}", "KeyA", ""),
520            ("KeyA\u{0001}ValueA\u{0002}KeyA\u{0001}ValueA", "KeyA", ""),
521            ("KeyA\u{0001}ValueA\u{0002}KeyA\u{0001}ValueA\u{0002}", "KeyA", ""),
522            (
523                "KeyB\u{0001}ValueB\u{0002}KeyA\u{0001}ValueA",
524                "KeyA",
525                "KeyB\u{0001}ValueB\u{0002}",
526            ),
527            (
528                "KeyB\u{0001}ValueB\u{0002}KeyA\u{0001}ValueA\u{0002}",
529                "KeyA",
530                "KeyB\u{0001}ValueB\u{0002}",
531            ),
532            (
533                "KeyB\u{0001}ValueB\u{0002}KeyA\u{0001}ValueA\u{0002}KeyB\u{0001}ValueB\u{0002}",
534                "KeyA",
535                "KeyB\u{0001}ValueB\u{0002}KeyB\u{0001}ValueB\u{0002}",
536            ),
537            (
538                "KeyA\u{0001}ValueA\u{0002}KeyB\u{0001}ValueB\u{0002}",
539                "KeyA",
540                "KeyB\u{0001}ValueB\u{0002}",
541            ),
542            (
543                "KeyA\u{0001}ValueA\u{0002}KeyB\u{0001}ValueB",
544                "KeyA",
545                "KeyB\u{0001}ValueB",
546            ),
547            (
548                "KeyA\u{0001}ValueA\u{0002}KeyB\u{0001}ValueBKeyA\u{0001}ValueA\u{0002}",
549                "KeyA",
550                "KeyB\u{0001}ValueBKeyA\u{0001}ValueA\u{0002}",
551            ),
552            (
553                "KeyA\u{0001}ValueA\u{0002}KeyB\u{0001}ValueBKeyA\u{0001}",
554                "KeyA",
555                "KeyB\u{0001}ValueBKeyA\u{0001}",
556            ),
557            (
558                "KeyA\u{0001}ValueA\u{0002}KeyB\u{0001}ValueBKeyA",
559                "KeyA",
560                "KeyB\u{0001}ValueBKeyA",
561            ),
562            ("KeyA\u{0001}ValueA", "", "KeyA\u{0001}ValueA"),
563        ];
564
565        for (properties, name, expected) in cases {
566            assert_eq!(delete_property(properties, name), expected);
567            assert_eq!(delete_property_v2(properties, name), expected);
568        }
569    }
570
571    #[test]
572    fn delete_property_to_cheetah_string_matches_string_deletion() {
573        let cases = [
574            (
575                "key1\u{0001}value1\u{0002}key2\u{0001}value2\u{0002}key3\u{0001}value3",
576                "key2",
577            ),
578            ("key1\u{0001}value1\u{0002}key2\u{0001}value2", "key3"),
579            ("Order\u{0001}remove\u{0002}OrderId\u{0001}keep", "Order"),
580            ("key\u{0001}value", ""),
581        ];
582
583        for (properties, name) in cases {
584            let properties = CheetahString::from_static_str(properties);
585
586            assert_eq!(
587                delete_property_to_cheetah_string(&properties, name),
588                delete_property(properties.as_str(), name)
589            );
590        }
591    }
592
593    #[test]
594    fn test_build_message_id() {
595        let socket_addr = "127.0.0.1:12".parse().unwrap();
596        let wrote_offset = 1;
597        let result = build_message_id(socket_addr, wrote_offset);
598        assert_eq!(result, "7F0000010000000C0000000000000001");
599    }
600
601    #[test]
602    fn build_batch_message_id_creates_correct_id_for_single_position() {
603        let socket_addr = SocketAddr::new(Ipv4Addr::new(127, 0, 0, 1).into(), 8080);
604        let store_host_length = 8;
605        let batch_size = 1;
606        let phy_pos = vec![12345];
607
608        let result = build_batch_message_id(socket_addr, store_host_length, batch_size, &phy_pos);
609
610        assert_eq!(result, "7F00000100001F900000000000003039");
611    }
612
613    #[test]
614    fn build_batch_message_id_creates_correct_id_for_multiple_positions() {
615        let socket_addr = SocketAddr::new(Ipv4Addr::new(127, 0, 0, 1).into(), 8080);
616        let store_host_length = 8;
617        let batch_size = 2;
618        let phy_pos = vec![12345, 67890];
619
620        let result = build_batch_message_id(socket_addr, store_host_length, batch_size, &phy_pos);
621
622        assert_eq!(
623            result,
624            "7F00000100001F900000000000003039,7F00000100001F900000000000010932"
625        );
626    }
627
628    #[test]
629    fn build_batch_message_id_creates_empty_id_for_no_positions() {
630        let socket_addr = SocketAddr::new(Ipv4Addr::new(127, 0, 0, 1).into(), 8080);
631        let store_host_length = 8;
632        let batch_size = 0;
633        let phy_pos = vec![];
634
635        let result = build_batch_message_id(socket_addr, store_host_length, batch_size, &phy_pos);
636
637        assert_eq!(result, "");
638    }
639}