1use simdnbt::owned::NbtTag;
18
19use crate::nbt::nbt_list_values;
20
21#[repr(u8)]
23#[derive(Clone, Copy)]
24pub enum HashTag {
25 Empty = 1,
27 MapStart = 2,
29 MapEnd = 3,
31 ListStart = 4,
33 ListEnd = 5,
35 Byte = 6,
37 Short = 7,
39 Int = 8,
41 Long = 9,
43 Float = 10,
45 Double = 11,
47 String = 12,
49 Boolean = 13,
51 ByteArrayStart = 14,
53 ByteArrayEnd = 15,
55 IntArrayStart = 16,
57 IntArrayEnd = 17,
59 LongArrayStart = 18,
61 LongArrayEnd = 19,
63}
64
65#[derive(Default)]
80pub struct ComponentHasher {
81 data: Vec<u8>,
82}
83
84impl ComponentHasher {
85 #[must_use]
87 pub const fn new() -> Self {
88 Self { data: Vec::new() }
89 }
90
91 fn put_tag(&mut self, tag: HashTag) {
93 self.data.push(tag as u8);
94 }
95
96 pub fn put_raw_bytes(&mut self, bytes: &[u8]) {
98 self.data.extend_from_slice(bytes);
99 }
100
101 pub fn put_component_hash<T: HashComponent + ?Sized>(&mut self, value: &T) {
105 self.put_raw_bytes(&value.compute_hash().to_le_bytes());
106 }
107
108 pub fn put_empty(&mut self) {
110 self.put_tag(HashTag::Empty);
111 }
112
113 pub fn put_byte(&mut self, value: i8) {
115 self.put_tag(HashTag::Byte);
116 self.data.push(value as u8);
117 }
118
119 pub fn put_ubyte(&mut self, value: u8) {
121 self.put_tag(HashTag::Byte);
122 self.data.push(value);
123 }
124
125 pub fn put_short(&mut self, value: i16) {
128 self.put_tag(HashTag::Short);
129 self.data.extend_from_slice(&value.to_le_bytes());
130 }
131
132 pub fn put_int(&mut self, value: i32) {
135 self.put_tag(HashTag::Int);
136 self.data.extend_from_slice(&value.to_le_bytes());
137 }
138
139 pub fn put_long(&mut self, value: i64) {
142 self.put_tag(HashTag::Long);
143 self.data.extend_from_slice(&value.to_le_bytes());
144 }
145
146 pub fn put_float(&mut self, value: f32) {
149 self.put_tag(HashTag::Float);
150 self.data.extend_from_slice(&value.to_bits().to_le_bytes());
151 }
152
153 pub fn put_double(&mut self, value: f64) {
156 self.put_tag(HashTag::Double);
157 self.data.extend_from_slice(&value.to_bits().to_le_bytes());
158 }
159
160 pub fn put_bool(&mut self, value: bool) {
162 self.put_tag(HashTag::Boolean);
163 self.data.push(u8::from(value));
164 }
165
166 pub fn put_string(&mut self, value: &str) {
172 self.put_tag(HashTag::String);
173 let char_count: i32 = value.chars().map(|c| c.len_utf16() as i32).sum();
176 self.data.extend_from_slice(&char_count.to_le_bytes());
177 for c in value.chars() {
180 let mut buf = [0u16; 2];
181 let encoded = c.encode_utf16(&mut buf);
182 for code_unit in encoded {
183 self.data.extend_from_slice(&code_unit.to_le_bytes());
184 }
185 }
186 }
187
188 pub fn start_map(&mut self) {
190 self.put_tag(HashTag::MapStart);
191 }
192
193 pub fn end_map(&mut self) {
195 self.put_tag(HashTag::MapEnd);
196 }
197
198 pub fn start_list(&mut self) {
200 self.put_tag(HashTag::ListStart);
201 }
202
203 pub fn end_list(&mut self) {
205 self.put_tag(HashTag::ListEnd);
206 }
207
208 pub fn start_byte_array(&mut self) {
210 self.put_tag(HashTag::ByteArrayStart);
211 }
212
213 pub fn end_byte_array(&mut self) {
215 self.put_tag(HashTag::ByteArrayEnd);
216 }
217
218 pub fn start_int_array(&mut self) {
220 self.put_tag(HashTag::IntArrayStart);
221 }
222
223 pub fn put_int_raw(&mut self, value: i32) {
226 self.data.extend_from_slice(&value.to_le_bytes());
227 }
228
229 pub fn end_int_array(&mut self) {
231 self.put_tag(HashTag::IntArrayEnd);
232 }
233
234 pub fn start_long_array(&mut self) {
236 self.put_tag(HashTag::LongArrayStart);
237 }
238
239 pub fn put_long_raw(&mut self, value: i64) {
242 self.data.extend_from_slice(&value.to_le_bytes());
243 }
244
245 pub fn end_long_array(&mut self) {
247 self.put_tag(HashTag::LongArrayEnd);
248 }
249
250 pub fn put_byte_array(&mut self, bytes: &[u8]) {
252 self.start_byte_array();
253 self.data.extend_from_slice(bytes);
254 self.end_byte_array();
255 }
256
257 pub fn put_int_array(&mut self, values: &[i32]) {
259 self.start_int_array();
260 for &v in values {
261 self.put_int_raw(v);
262 }
263 self.end_int_array();
264 }
265
266 pub fn put_long_array(&mut self, values: &[i64]) {
268 self.start_long_array();
269 for &v in values {
270 self.put_long_raw(v);
271 }
272 self.end_long_array();
273 }
274
275 #[must_use]
277 pub fn current_data(&self) -> &[u8] {
278 &self.data
279 }
280
281 #[must_use]
283 pub fn finish(self) -> i32 {
284 crc32c::crc32c(&self.data) as i32
285 }
286}
287
288#[derive(Clone)]
293pub struct HashEntry {
294 pub key_hash: i64,
296 pub value_hash: i64,
298 pub key_bytes: [u8; 4],
300 pub value_bytes: [u8; 4],
302}
303
304impl HashEntry {
305 #[must_use]
307 pub fn new(key_hasher: ComponentHasher, value_hasher: ComponentHasher) -> Self {
308 let key_bytes = crc32c::crc32c(&key_hasher.data);
309 let value_bytes = crc32c::crc32c(&value_hasher.data);
310 Self::from_hashes(key_bytes, value_bytes)
311 }
312
313 #[must_use]
315 pub const fn from_hashes(key_hash: u32, value_hash: u32) -> Self {
316 Self {
317 key_hash: key_hash as i64,
318 value_hash: value_hash as i64,
319 key_bytes: key_hash.to_le_bytes(),
320 value_bytes: value_hash.to_le_bytes(),
321 }
322 }
323}
324
325pub fn sort_map_entries(entries: &mut [HashEntry]) {
328 entries.sort_by(|a, b| {
329 a.key_hash
330 .cmp(&b.key_hash)
331 .then_with(|| a.value_hash.cmp(&b.value_hash))
332 });
333}
334
335pub trait HashComponent {
337 fn hash_component(&self, hasher: &mut ComponentHasher);
339
340 fn compute_hash(&self) -> i32 {
342 let mut hasher = ComponentHasher::new();
343 self.hash_component(&mut hasher);
344 hasher.finish()
345 }
346}
347
348impl HashComponent for i8 {
350 fn hash_component(&self, hasher: &mut ComponentHasher) {
351 hasher.put_byte(*self);
352 }
353}
354
355impl HashComponent for u8 {
356 fn hash_component(&self, hasher: &mut ComponentHasher) {
357 hasher.put_ubyte(*self);
358 }
359}
360
361impl HashComponent for i16 {
362 fn hash_component(&self, hasher: &mut ComponentHasher) {
363 hasher.put_short(*self);
364 }
365}
366
367impl HashComponent for i32 {
368 fn hash_component(&self, hasher: &mut ComponentHasher) {
369 hasher.put_int(*self);
370 }
371}
372
373impl HashComponent for i64 {
374 fn hash_component(&self, hasher: &mut ComponentHasher) {
375 hasher.put_long(*self);
376 }
377}
378
379impl HashComponent for f32 {
380 fn hash_component(&self, hasher: &mut ComponentHasher) {
381 hasher.put_float(*self);
382 }
383}
384
385impl HashComponent for f64 {
386 fn hash_component(&self, hasher: &mut ComponentHasher) {
387 hasher.put_double(*self);
388 }
389}
390
391impl HashComponent for bool {
392 fn hash_component(&self, hasher: &mut ComponentHasher) {
393 hasher.put_bool(*self);
394 }
395}
396
397impl HashComponent for str {
398 fn hash_component(&self, hasher: &mut ComponentHasher) {
399 hasher.put_string(self);
400 }
401}
402
403impl HashComponent for String {
404 fn hash_component(&self, hasher: &mut ComponentHasher) {
405 hasher.put_string(self);
406 }
407}
408
409impl HashComponent for () {
410 fn hash_component(&self, hasher: &mut ComponentHasher) {
411 hasher.start_map();
412 hasher.end_map();
413 }
414}
415
416impl HashComponent for NbtTag {
417 fn hash_component(&self, hasher: &mut ComponentHasher) {
418 match self {
419 NbtTag::Byte(value) => hasher.put_byte(*value),
420 NbtTag::Short(value) => hasher.put_short(*value),
421 NbtTag::Int(value) => hasher.put_int(*value),
422 NbtTag::Long(value) => hasher.put_long(*value),
423 NbtTag::Float(value) => hasher.put_float(*value),
424 NbtTag::Double(value) => hasher.put_double(*value),
425 NbtTag::ByteArray(values) => hasher.put_byte_array(values),
426 NbtTag::String(value) => hasher.put_string(&value.to_string()),
427 NbtTag::List(values) => {
428 hasher.start_list();
429 for value in nbt_list_values(values) {
430 hasher.put_component_hash(&value);
431 }
432 hasher.end_list();
433 }
434 NbtTag::Compound(values) => {
435 let mut entries = values
436 .iter()
437 .map(|(key, value)| {
438 let mut key_hasher = ComponentHasher::new();
439 key_hasher.put_string(&key.to_string());
440 let mut value_hasher = ComponentHasher::new();
441 value.hash_component(&mut value_hasher);
442 HashEntry::new(key_hasher, value_hasher)
443 })
444 .collect::<Vec<_>>();
445 sort_map_entries(&mut entries);
446
447 hasher.start_map();
448 for entry in entries {
449 hasher.put_raw_bytes(&entry.key_bytes);
450 hasher.put_raw_bytes(&entry.value_bytes);
451 }
452 hasher.end_map();
453 }
454 NbtTag::IntArray(values) => hasher.put_int_array(values),
455 NbtTag::LongArray(values) => hasher.put_long_array(values),
456 }
457 }
458}
459
460#[cfg(test)]
461mod tests {
462 use super::*;
463
464 #[test]
465 fn test_int_hash() {
466 let mut hasher = ComponentHasher::new();
467 hasher.put_int(42);
468 let hash = hasher.finish();
469 assert_ne!(hash, 0);
471 }
472
473 #[test]
474 fn test_string_hash() {
475 let mut hasher = ComponentHasher::new();
476 hasher.put_string("hello");
477 let hash = hasher.finish();
478 assert_ne!(hash, 0);
480 }
481
482 #[test]
483 fn test_bool_hash() {
484 let mut hasher_true = ComponentHasher::new();
485 hasher_true.put_bool(true);
486 let hash_true = hasher_true.finish();
487
488 let mut hasher_false = ComponentHasher::new();
489 hasher_false.put_bool(false);
490 let hash_false = hasher_false.finish();
491
492 assert_ne!(hash_true, hash_false);
494 }
495
496 #[test]
497 fn test_empty_map_hash() {
498 let mut hasher = ComponentHasher::new();
499 hasher.start_map();
500 hasher.end_map();
501 let hash = hasher.finish();
502 assert_ne!(hash, 0);
504 }
505
506 #[test]
507 fn unit_codec_hashes_as_an_empty_map() {
508 let mut hasher = ComponentHasher::new();
509 hasher.start_map();
510 hasher.end_map();
511
512 assert_eq!(().compute_hash(), hasher.finish());
513 }
514
515 #[test]
516 fn test_empty_list_hash() {
517 let mut hasher = ComponentHasher::new();
518 hasher.start_list();
519 hasher.end_list();
520 let hash = hasher.finish();
521 assert_ne!(hash, 0);
523 }
524
525 #[test]
526 fn test_byte_array_hash() {
527 let mut hasher = ComponentHasher::new();
528 hasher.put_byte_array(&[1, 2, 3, 4]);
529 let hash = hasher.finish();
530 assert_ne!(hash, 0);
532 }
533
534 #[test]
535 fn test_deterministic() {
536 let hash1 = {
538 let mut h = ComponentHasher::new();
539 h.put_int(12345);
540 h.put_string("test");
541 h.finish()
542 };
543 let hash2 = {
544 let mut h = ComponentHasher::new();
545 h.put_int(12345);
546 h.put_string("test");
547 h.finish()
548 };
549 assert_eq!(hash1, hash2);
550 }
551
552 #[test]
553 fn nbt_compound_hash_is_independent_of_entry_order() {
554 use simdnbt::owned::NbtCompound;
555
556 let first = NbtTag::Compound(NbtCompound::from_values(vec![
557 ("first".into(), NbtTag::Int(1)),
558 ("second".into(), NbtTag::String("two".into())),
559 ]));
560 let reversed = NbtTag::Compound(NbtCompound::from_values(vec![
561 ("second".into(), NbtTag::String("two".into())),
562 ("first".into(), NbtTag::Int(1)),
563 ]));
564
565 assert_eq!(first.compute_hash(), reversed.compute_hash());
566 }
567
568 #[test]
569 fn nbt_list_hash_unwraps_vanillas_heterogeneous_list_marker() {
570 use simdnbt::owned::{NbtCompound, NbtList};
571
572 let mut wrapper = NbtCompound::new();
573 wrapper.insert("", 7);
574 let encoded = NbtTag::List(NbtList::Compound(vec![wrapper]));
575
576 let mut expected = ComponentHasher::new();
577 expected.start_list();
578 expected.put_component_hash(&NbtTag::Int(7));
579 expected.end_list();
580
581 assert_eq!(encoded.compute_hash(), expected.finish());
582 }
583
584 #[test]
585 fn test_text_component_steel() {
586 use text_components::TextComponent;
587
588 let component = TextComponent::from("Steel");
590 let hash = component.compute_hash();
591
592 assert_eq!(hash, -25_646_594, "Hash should match vanilla client");
594 }
595
596 #[test]
597 fn test_text_component_simple_styled() {
598 use text_components::TextComponent;
599 use text_components::{Modifier, format::Color};
600
601 let component = TextComponent::plain("R").color(Color::Red).bold(true);
604 let hash = component.compute_hash();
605
606 assert_eq!(
607 hash, 1_605_556_242,
608 "Hash should match vanilla client 1.21.11 for simple styled text"
609 );
610 }
611
612 #[test]
613 fn test_text_component_rainbow() {
614 use text_components::TextComponent;
615
616 let json = r##"[{"text":"R","color":"red","bold":true},{"text":"a","color":"#ff5a00"},{"text":"i","color":"yellow","bold":true},{"text":"n","color":"green"},{"text":"b","color":"aqua","bold":true},{"text":"o","color":"blue"},{"text":"w","color":"light_purple","bold":true}]"##;
618 let component = TextComponent::from_snbt(json).expect("Failed to parse rainbow text");
619 let hash = component.compute_hash();
620
621 assert_eq!(
623 hash, 796_582_470,
624 "Hash should match vanilla client for rainbow text"
625 );
626 }
627}