1use crate::flags::{KeyFlags, ValueFlags, ValueType};
4use binrw::BinRead;
5
6#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, BinRead)]
12#[br(little)]
13pub struct CellOffset(pub u32);
14
15impl CellOffset {
16 pub const NULL: Self = Self(0xFFFF_FFFF);
18
19 pub fn file_offset(self) -> u64 {
23 4096 + u64::from(self.0)
24 }
25
26 pub fn is_null(self) -> bool {
28 self.0 == 0xFFFF_FFFF
29 }
30}
31
32impl std::fmt::Display for CellOffset {
33 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
34 if self.is_null() {
35 write!(f, "NULL")
36 } else {
37 write!(f, "0x{:08X}", self.0)
38 }
39 }
40}
41
42#[derive(Debug, Clone, Copy)]
50pub struct CellHeader {
51 pub raw_size: i32,
53}
54
55impl CellHeader {
56 pub fn from_bytes(bytes: &[u8; 4]) -> Self {
58 Self {
59 raw_size: i32::from_le_bytes(*bytes),
60 }
61 }
62
63 pub fn is_allocated(&self) -> bool {
65 self.raw_size < 0
66 }
67
68 pub fn size(&self) -> u32 {
70 self.raw_size.unsigned_abs()
71 }
72}
73
74#[derive(Debug, Clone, Copy, PartialEq, Eq)]
76pub enum CellSignature {
77 KeyNode,
79 KeyValue,
81 SecurityKey,
83 FastLeaf,
85 HashLeaf,
87 IndexLeaf,
89 RootIndex,
91 BigData,
93}
94
95impl CellSignature {
96 pub fn from_bytes(bytes: &[u8; 2]) -> Option<Self> {
98 match bytes {
99 b"nk" => Some(Self::KeyNode),
100 b"vk" => Some(Self::KeyValue),
101 b"sk" => Some(Self::SecurityKey),
102 b"lf" => Some(Self::FastLeaf),
103 b"lh" => Some(Self::HashLeaf),
104 b"li" => Some(Self::IndexLeaf),
105 b"ri" => Some(Self::RootIndex),
106 b"db" => Some(Self::BigData),
107 _ => None,
108 }
109 }
110}
111
112#[cfg(test)]
113mod tests {
114 use super::*;
115
116 #[test]
117 fn cell_offset_file_conversion() {
118 let offset = CellOffset(0x20);
119 assert_eq!(offset.file_offset(), 4096 + 0x20);
120 }
121
122 #[test]
123 fn cell_offset_null() {
124 assert!(CellOffset::NULL.is_null());
125 assert!(!CellOffset(0).is_null());
126 }
127
128 #[test]
129 fn cell_offset_display() {
130 assert_eq!(format!("{}", CellOffset::NULL), "NULL");
131 assert_eq!(format!("{}", CellOffset(0x20)), "0x00000020");
132 }
133
134 #[test]
135 fn cell_header_allocated() {
136 let bytes = (-128i32).to_le_bytes();
137 let header = CellHeader::from_bytes(&bytes);
138 assert!(header.is_allocated());
139 assert_eq!(header.size(), 128);
140 }
141
142 #[test]
143 fn cell_header_free() {
144 let bytes = 64i32.to_le_bytes();
145 let header = CellHeader::from_bytes(&bytes);
146 assert!(!header.is_allocated());
147 assert_eq!(header.size(), 64);
148 }
149
150 #[test]
151 fn cell_signatures() {
152 assert_eq!(
153 CellSignature::from_bytes(b"nk"),
154 Some(CellSignature::KeyNode)
155 );
156 assert_eq!(
157 CellSignature::from_bytes(b"vk"),
158 Some(CellSignature::KeyValue)
159 );
160 assert_eq!(
161 CellSignature::from_bytes(b"sk"),
162 Some(CellSignature::SecurityKey)
163 );
164 assert_eq!(
165 CellSignature::from_bytes(b"lf"),
166 Some(CellSignature::FastLeaf)
167 );
168 assert_eq!(
169 CellSignature::from_bytes(b"lh"),
170 Some(CellSignature::HashLeaf)
171 );
172 assert_eq!(
173 CellSignature::from_bytes(b"li"),
174 Some(CellSignature::IndexLeaf)
175 );
176 assert_eq!(
177 CellSignature::from_bytes(b"ri"),
178 Some(CellSignature::RootIndex)
179 );
180 assert_eq!(
181 CellSignature::from_bytes(b"db"),
182 Some(CellSignature::BigData)
183 );
184 assert_eq!(CellSignature::from_bytes(b"xx"), None);
185 }
186}
187
188#[derive(Debug, Clone)]
192pub struct RawKeyNode {
193 pub flags: KeyFlags,
194 pub last_written: u64,
195 pub access_bits: u32,
196 pub parent: CellOffset,
197 pub subkey_count: u32,
198 pub volatile_subkey_count: u32,
199 pub subkeys_list_offset: CellOffset,
200 pub volatile_subkeys_list_offset: CellOffset,
201 pub value_count: u32,
202 pub values_list_offset: CellOffset,
203 pub security_offset: CellOffset,
204 pub class_name_offset: CellOffset,
205 pub max_subkey_name_compound: u32,
206 pub max_subkey_class_len: u32,
207 pub max_value_name_len: u32,
208 pub max_value_data_size: u32,
209 pub work_var: u32,
210 pub key_name_len: u16,
211 pub class_name_len: u16,
212 pub key_name_raw: Vec<u8>,
213}
214
215impl RawKeyNode {
216 pub const HEADER_SIZE: usize = 0x4C;
217
218 pub fn parse(data: &[u8]) -> Option<Self> {
220 if data.len() < Self::HEADER_SIZE - 2 {
221 return None;
222 }
223 let flags = KeyFlags::from_bits_truncate(u16::from_le_bytes([data[0], data[1]]));
224 let last_written = u64::from_le_bytes(data[2..10].try_into().ok()?);
225 let access_bits = u32::from_le_bytes(data[10..14].try_into().ok()?);
226 let parent = CellOffset(u32::from_le_bytes(data[14..18].try_into().ok()?));
227 let subkey_count = u32::from_le_bytes(data[18..22].try_into().ok()?);
228 let volatile_subkey_count = u32::from_le_bytes(data[22..26].try_into().ok()?);
229 let subkeys_list_offset = CellOffset(u32::from_le_bytes(data[26..30].try_into().ok()?));
230 let volatile_subkeys_list_offset =
231 CellOffset(u32::from_le_bytes(data[30..34].try_into().ok()?));
232 let value_count = u32::from_le_bytes(data[34..38].try_into().ok()?);
233 let values_list_offset = CellOffset(u32::from_le_bytes(data[38..42].try_into().ok()?));
234 let security_offset = CellOffset(u32::from_le_bytes(data[42..46].try_into().ok()?));
235 let class_name_offset = CellOffset(u32::from_le_bytes(data[46..50].try_into().ok()?));
236 let max_subkey_name_compound = u32::from_le_bytes(data[50..54].try_into().ok()?);
237 let max_subkey_class_len = u32::from_le_bytes(data[54..58].try_into().ok()?);
238 let max_value_name_len = u32::from_le_bytes(data[58..62].try_into().ok()?);
239 let max_value_data_size = u32::from_le_bytes(data[62..66].try_into().ok()?);
240 let work_var = u32::from_le_bytes(data[66..70].try_into().ok()?);
241 let key_name_len = u16::from_le_bytes([data[70], data[71]]);
242 let class_name_len = u16::from_le_bytes([data[72], data[73]]);
243
244 let name_start = 74;
245 let name_end = name_start + usize::from(key_name_len);
246 if data.len() < name_end {
247 return None;
248 }
249 let key_name_raw = data[name_start..name_end].to_vec();
250
251 Some(Self {
252 flags,
253 last_written,
254 access_bits,
255 parent,
256 subkey_count,
257 volatile_subkey_count,
258 subkeys_list_offset,
259 volatile_subkeys_list_offset,
260 value_count,
261 values_list_offset,
262 security_offset,
263 class_name_offset,
264 max_subkey_name_compound,
265 max_subkey_class_len,
266 max_value_name_len,
267 max_value_data_size,
268 work_var,
269 key_name_len,
270 class_name_len,
271 key_name_raw,
272 })
273 }
274
275 pub fn key_name(&self) -> String {
276 if self.flags.contains(KeyFlags::COMP_NAME) {
277 self.key_name_raw.iter().map(|&b| b as char).collect()
278 } else {
279 let u16s: Vec<u16> = self
280 .key_name_raw
281 .chunks_exact(2)
282 .map(|c| u16::from_le_bytes([c[0], c[1]]))
283 .collect();
284 String::from_utf16_lossy(&u16s)
285 }
286 }
287
288 pub fn is_root(&self) -> bool {
289 self.flags.contains(KeyFlags::HIVE_ENTRY)
290 }
291}
292
293#[derive(Debug, Clone)]
297pub struct RawKeyValue {
298 pub name_len: u16,
299 pub data_size_raw: u32,
300 pub data_offset_raw: u32,
301 pub data_type: ValueType,
302 pub flags: ValueFlags,
303 pub name_raw: Vec<u8>,
304}
305
306impl RawKeyValue {
307 pub const HEADER_SIZE: usize = 0x14;
308
309 pub fn parse(data: &[u8]) -> Option<Self> {
310 if data.len() < Self::HEADER_SIZE - 2 {
311 return None;
312 }
313 let name_len = u16::from_le_bytes([data[0], data[1]]);
314 let data_size_raw = u32::from_le_bytes(data[2..6].try_into().ok()?);
315 let data_offset_raw = u32::from_le_bytes(data[6..10].try_into().ok()?);
316 let data_type = ValueType::from_raw(u32::from_le_bytes(data[10..14].try_into().ok()?));
317 let flags = ValueFlags::from_bits_truncate(u16::from_le_bytes([data[14], data[15]]));
318
319 let name_start = 18;
320 let name_end = name_start + usize::from(name_len);
321 if data.len() < name_end {
322 return None;
323 }
324 let name_raw = data[name_start..name_end].to_vec();
325
326 Some(Self {
327 name_len,
328 data_size_raw,
329 data_offset_raw,
330 data_type,
331 flags,
332 name_raw,
333 })
334 }
335
336 pub fn is_resident(&self) -> bool {
337 self.data_size_raw & 0x8000_0000 != 0
338 }
339
340 pub fn data_size(&self) -> u32 {
341 self.data_size_raw & 0x7FFF_FFFF
342 }
343
344 pub fn data_offset(&self) -> CellOffset {
345 CellOffset(self.data_offset_raw)
346 }
347
348 pub fn inline_data(&self) -> Vec<u8> {
349 let size = self.data_size() as usize;
350 let bytes = self.data_offset_raw.to_le_bytes();
351 bytes[..size.min(4)].to_vec()
352 }
353
354 pub fn value_name(&self) -> String {
355 if self.name_len == 0 {
356 return String::new();
357 }
358 if self.flags.contains(ValueFlags::COMP_NAME) {
359 self.name_raw.iter().map(|&b| b as char).collect()
360 } else {
361 let u16s: Vec<u16> = self
362 .name_raw
363 .chunks_exact(2)
364 .map(|c| u16::from_le_bytes([c[0], c[1]]))
365 .collect();
366 String::from_utf16_lossy(&u16s)
367 }
368 }
369}
370
371#[cfg(test)]
372mod nk_vk_tests {
373 use super::*;
374 use crate::flags::KeyFlags;
375
376 fn build_nk_bytes(name: &str, flags: KeyFlags, subkey_count: u32, value_count: u32) -> Vec<u8> {
377 let name_bytes = name.as_bytes();
378 let mut buf = vec![0u8; 74 + name_bytes.len()];
379 buf[0..2].copy_from_slice(&flags.bits().to_le_bytes());
380 buf[2..10].copy_from_slice(&1000u64.to_le_bytes());
381 buf[14..18].copy_from_slice(&0x20u32.to_le_bytes());
382 buf[18..22].copy_from_slice(&subkey_count.to_le_bytes());
383 let sk_offset = if subkey_count > 0 {
384 0x100u32
385 } else {
386 0xFFFF_FFFFu32
387 };
388 buf[26..30].copy_from_slice(&sk_offset.to_le_bytes());
389 buf[34..38].copy_from_slice(&value_count.to_le_bytes());
390 let vl_offset = if value_count > 0 {
391 0x200u32
392 } else {
393 0xFFFF_FFFFu32
394 };
395 buf[38..42].copy_from_slice(&vl_offset.to_le_bytes());
396 buf[42..46].copy_from_slice(&0x300u32.to_le_bytes());
397 buf[46..50].copy_from_slice(&0xFFFF_FFFFu32.to_le_bytes());
398 buf[70..72].copy_from_slice(&(name_bytes.len() as u16).to_le_bytes());
399 buf[74..74 + name_bytes.len()].copy_from_slice(name_bytes);
400 buf
401 }
402
403 #[test]
404 fn parse_nk_root_key() {
405 let data = build_nk_bytes(
406 "CMI-CreateHive{2A7FB991}",
407 KeyFlags::HIVE_ENTRY | KeyFlags::COMP_NAME,
408 3,
409 0,
410 );
411 let nk = RawKeyNode::parse(&data).unwrap();
412 assert!(nk.is_root());
413 assert_eq!(nk.key_name(), "CMI-CreateHive{2A7FB991}");
414 assert_eq!(nk.subkey_count, 3);
415 assert_eq!(nk.value_count, 0);
416 assert!(nk.flags.contains(KeyFlags::COMP_NAME));
417 }
418
419 #[test]
420 fn parse_nk_child_key() {
421 let data = build_nk_bytes("Software", KeyFlags::COMP_NAME, 0, 2);
422 let nk = RawKeyNode::parse(&data).unwrap();
423 assert!(!nk.is_root());
424 assert_eq!(nk.key_name(), "Software");
425 assert_eq!(nk.value_count, 2);
426 }
427
428 #[test]
429 fn nk_rejects_truncated_data() {
430 let data = vec![0u8; 10];
431 assert!(RawKeyNode::parse(&data).is_none());
432 }
433
434 fn build_vk_bytes(name: &str, data_type: u32, data_size: u32, data_offset: u32) -> Vec<u8> {
435 let name_bytes = name.as_bytes();
436 let comp_flag: u16 = if name.is_empty() { 0 } else { 0x0001 };
437 let mut buf = vec![0u8; 18 + name_bytes.len()];
438 buf[0..2].copy_from_slice(&(name_bytes.len() as u16).to_le_bytes());
439 buf[2..6].copy_from_slice(&data_size.to_le_bytes());
440 buf[6..10].copy_from_slice(&data_offset.to_le_bytes());
441 buf[10..14].copy_from_slice(&data_type.to_le_bytes());
442 buf[14..16].copy_from_slice(&comp_flag.to_le_bytes());
443 buf[18..18 + name_bytes.len()].copy_from_slice(name_bytes);
444 buf
445 }
446
447 #[test]
448 fn parse_vk_dword_resident() {
449 let data = build_vk_bytes("Start", 4, 0x8000_0004, 0x0000_0003);
450 let vk = RawKeyValue::parse(&data).unwrap();
451 assert_eq!(vk.value_name(), "Start");
452 assert!(matches!(vk.data_type, ValueType::Dword));
453 assert!(vk.is_resident());
454 assert_eq!(vk.data_size(), 4);
455 assert_eq!(vk.inline_data(), vec![3, 0, 0, 0]);
456 }
457
458 #[test]
459 fn parse_vk_string_non_resident() {
460 let data = build_vk_bytes("ImagePath", 1, 42, 0x500);
461 let vk = RawKeyValue::parse(&data).unwrap();
462 assert_eq!(vk.value_name(), "ImagePath");
463 assert!(matches!(vk.data_type, ValueType::Sz));
464 assert!(!vk.is_resident());
465 assert_eq!(vk.data_size(), 42);
466 assert_eq!(vk.data_offset(), CellOffset(0x500));
467 }
468
469 #[test]
470 fn parse_vk_unnamed_default_value() {
471 let data = build_vk_bytes("", 1, 10, 0x600);
472 let vk = RawKeyValue::parse(&data).unwrap();
473 assert_eq!(vk.value_name(), "");
474 assert_eq!(vk.name_len, 0);
475 }
476
477 #[test]
478 fn vk_rejects_truncated_data() {
479 let data = vec![0u8; 5];
480 assert!(RawKeyValue::parse(&data).is_none());
481 }
482}
483
484#[derive(Debug, Clone, Copy)]
486pub struct LfElement {
487 pub key_offset: CellOffset,
488 pub name_hint: [u8; 4],
490}
491
492#[derive(Debug, Clone, Copy)]
494pub struct LhElement {
495 pub key_offset: CellOffset,
496 pub name_hash: u32,
498}
499
500#[derive(Debug, Clone)]
502pub enum SubkeyIndex {
503 FastLeaf(Vec<LfElement>),
504 HashLeaf(Vec<LhElement>),
505 IndexLeaf(Vec<CellOffset>),
506 RootIndex(Vec<CellOffset>),
507}
508
509impl SubkeyIndex {
510 pub fn parse_lf(data: &[u8]) -> Option<Self> {
511 if data.len() < 2 {
512 return None;
513 }
514 let count = u16::from_le_bytes([data[0], data[1]]) as usize;
515 let elements_data = &data[2..];
516 if elements_data.len() < count * 8 {
517 return None;
518 }
519 let elements = (0..count)
520 .map(|i| {
521 let base = i * 8;
522 LfElement {
523 key_offset: CellOffset(crate::bytes::le_u32(elements_data, base)),
524 name_hint: crate::bytes::read4(elements_data, base + 4),
525 }
526 })
527 .collect();
528 Some(Self::FastLeaf(elements))
529 }
530
531 pub fn parse_lh(data: &[u8]) -> Option<Self> {
532 if data.len() < 2 {
533 return None;
534 }
535 let count = u16::from_le_bytes([data[0], data[1]]) as usize;
536 let elements_data = &data[2..];
537 if elements_data.len() < count * 8 {
538 return None;
539 }
540 let elements = (0..count)
541 .map(|i| {
542 let base = i * 8;
543 LhElement {
544 key_offset: CellOffset(crate::bytes::le_u32(elements_data, base)),
545 name_hash: crate::bytes::le_u32(elements_data, base + 4),
546 }
547 })
548 .collect();
549 Some(Self::HashLeaf(elements))
550 }
551
552 pub fn parse_li(data: &[u8]) -> Option<Self> {
553 if data.len() < 2 {
554 return None;
555 }
556 let count = u16::from_le_bytes([data[0], data[1]]) as usize;
557 let elements_data = &data[2..];
558 if elements_data.len() < count * 4 {
559 return None;
560 }
561 let offsets = (0..count)
562 .map(|i| {
563 let base = i * 4;
564 CellOffset(crate::bytes::le_u32(elements_data, base))
565 })
566 .collect();
567 Some(Self::IndexLeaf(offsets))
568 }
569
570 pub fn parse_ri(data: &[u8]) -> Option<Self> {
571 if data.len() < 2 {
572 return None;
573 }
574 let count = u16::from_le_bytes([data[0], data[1]]) as usize;
575 let elements_data = &data[2..];
576 if elements_data.len() < count * 4 {
577 return None;
578 }
579 let offsets = (0..count)
580 .map(|i| {
581 let base = i * 4;
582 CellOffset(crate::bytes::le_u32(elements_data, base))
583 })
584 .collect();
585 Some(Self::RootIndex(offsets))
586 }
587}
588
589pub fn lh_hash(name: &str) -> u32 {
591 let mut h: u32 = 0;
592 for c in name.to_ascii_uppercase().bytes() {
593 h = h.wrapping_mul(37).wrapping_add(u32::from(c));
594 }
595 h
596}
597
598#[derive(Debug, Clone)]
600pub struct RawSecurityKey {
601 pub flink: CellOffset,
602 pub blink: CellOffset,
603 pub reference_count: u32,
604 pub descriptor_size: u32,
605 pub descriptor: Vec<u8>,
606}
607
608impl RawSecurityKey {
609 pub fn parse(data: &[u8]) -> Option<Self> {
610 if data.len() < 18 {
611 return None;
612 }
613 let flink = CellOffset(u32::from_le_bytes(data[2..6].try_into().ok()?));
614 let blink = CellOffset(u32::from_le_bytes(data[6..10].try_into().ok()?));
615 let reference_count = u32::from_le_bytes(data[10..14].try_into().ok()?);
616 let descriptor_size = u32::from_le_bytes(data[14..18].try_into().ok()?);
617 let desc_end = 18 + descriptor_size as usize;
618 if data.len() < desc_end {
619 return None;
620 }
621 let descriptor = data[18..desc_end].to_vec();
622 Some(Self {
623 flink,
624 blink,
625 reference_count,
626 descriptor_size,
627 descriptor,
628 })
629 }
630}
631
632#[derive(Debug, Clone)]
634pub struct RawBigData {
635 pub segment_count: u16,
636 pub segment_list_offset: CellOffset,
637}
638
639impl RawBigData {
640 pub fn parse(data: &[u8]) -> Option<Self> {
641 if data.len() < 6 {
642 return None;
643 }
644 let segment_count = u16::from_le_bytes([data[0], data[1]]);
645 let segment_list_offset = CellOffset(u32::from_le_bytes(data[2..6].try_into().ok()?));
646 Some(Self {
647 segment_count,
648 segment_list_offset,
649 })
650 }
651}
652
653#[cfg(test)]
654mod index_tests {
655 use super::*;
656
657 #[test]
658 fn parse_lh_with_two_elements() {
659 let mut data = vec![0u8; 2 + 2 * 8];
660 data[0..2].copy_from_slice(&2u16.to_le_bytes());
661 data[2..6].copy_from_slice(&0x100u32.to_le_bytes());
662 data[6..10].copy_from_slice(&0xABCDu32.to_le_bytes());
663 data[10..14].copy_from_slice(&0x200u32.to_le_bytes());
664 data[14..18].copy_from_slice(&0x1234u32.to_le_bytes());
665 let index = SubkeyIndex::parse_lh(&data).unwrap();
666 if let SubkeyIndex::HashLeaf(elements) = index {
667 assert_eq!(elements.len(), 2);
668 assert_eq!(elements[0].key_offset, CellOffset(0x100));
669 assert_eq!(elements[0].name_hash, 0xABCD);
670 assert_eq!(elements[1].key_offset, CellOffset(0x200));
671 } else {
672 panic!("expected HashLeaf");
673 }
674 }
675
676 #[test]
677 fn parse_li_with_three_offsets() {
678 let mut data = vec![0u8; 2 + 3 * 4];
679 data[0..2].copy_from_slice(&3u16.to_le_bytes());
680 data[2..6].copy_from_slice(&0x100u32.to_le_bytes());
681 data[6..10].copy_from_slice(&0x200u32.to_le_bytes());
682 data[10..14].copy_from_slice(&0x300u32.to_le_bytes());
683 let index = SubkeyIndex::parse_li(&data).unwrap();
684 if let SubkeyIndex::IndexLeaf(offsets) = index {
685 assert_eq!(offsets.len(), 3);
686 assert_eq!(offsets[0], CellOffset(0x100));
687 } else {
688 panic!("expected IndexLeaf");
689 }
690 }
691
692 #[test]
693 fn lh_hash_algorithm() {
694 let hash = lh_hash("SOFTWARE");
695 assert_eq!(hash, lh_hash("software")); }
697
698 #[test]
699 fn parse_sk_cell() {
700 let mut data = vec![0u8; 18 + 20];
701 data[2..6].copy_from_slice(&0x100u32.to_le_bytes());
702 data[6..10].copy_from_slice(&0x200u32.to_le_bytes());
703 data[10..14].copy_from_slice(&3u32.to_le_bytes());
704 data[14..18].copy_from_slice(&20u32.to_le_bytes());
705 data[18..38].fill(0xAA);
706 let sk = RawSecurityKey::parse(&data).unwrap();
707 assert_eq!(sk.flink, CellOffset(0x100));
708 assert_eq!(sk.reference_count, 3);
709 assert_eq!(sk.descriptor.len(), 20);
710 }
711
712 #[test]
713 fn parse_db_cell() {
714 let mut data = vec![0u8; 6];
715 data[0..2].copy_from_slice(&3u16.to_le_bytes());
716 data[2..6].copy_from_slice(&0x500u32.to_le_bytes());
717 let db = RawBigData::parse(&data).unwrap();
718 assert_eq!(db.segment_count, 3);
719 assert_eq!(db.segment_list_offset, CellOffset(0x500));
720 }
721
722 #[test]
723 fn empty_index_parses() {
724 let data = vec![0u8; 2];
725 let index = SubkeyIndex::parse_lh(&data).unwrap();
726 if let SubkeyIndex::HashLeaf(elements) = index {
727 assert!(elements.is_empty());
728 } else {
729 panic!("expected empty HashLeaf");
730 }
731 }
732}