feat: 为 JsValue 添加字符串支持

This commit is contained in:
爱特每羊队
2026-06-05 17:22:29 +08:00
parent 20726da808
commit 1476572f99
+53 -1
View File
@@ -17,6 +17,7 @@ const TAG_BOOL: u64 = 1;
const TAG_NULL: u64 = 2;
const TAG_UNDEFINED: u64 = 3;
const TAG_OBJECT: u64 = 4;
const TAG_STRING: u64 = 5;
/// 48-bit pointer mask (x86-64 canonical VA)
const PTR_MASK: u64 = 0x0000_FFFF_FFFF_FFFF;
@@ -24,6 +25,11 @@ const PTR_MASK: u64 = 0x0000_FFFF_FFFF_FFFF;
/// 32-bit integer payload mask
const INT_MASK: u64 = 0x0000_0000_FFFF_FFFF;
/// String payload: [hash_prefix:16bit][table_index:32bit]
const STRING_HASH_SHIFT: u64 = 32;
const STRING_HASH_MASK: u64 = 0xFFFF_0000_0000;
const STRING_INDEX_MASK: u64 = 0x0000_FFFF_FFFF;
fn make_tag(tag: u64) -> u64 {
QNAN_PREFIX | (tag << TAG_SHIFT)
}
@@ -61,6 +67,14 @@ impl PartialEq for JsValue {
if self.is_object() && other.is_object() {
return self.as_ptr() == other.as_ptr();
}
if self.is_string() && other.is_string() {
let ha = self.as_string_hash();
let hb = other.as_string_hash();
if ha != hb {
return false;
}
return self.as_string_index() == other.as_string_index();
}
false
}
}
@@ -102,6 +116,15 @@ impl JsValue {
Self(make_tag(TAG_OBJECT) | addr)
}
pub fn string(index: u32, hash: u16) -> Self {
let payload = ((hash as u64) << STRING_HASH_SHIFT) | (index as u64);
debug_assert!(
payload & !(STRING_HASH_MASK | STRING_INDEX_MASK) == 0,
"string payload overflow"
);
Self(make_tag(TAG_STRING) | payload)
}
pub fn is_double(&self) -> bool {
!is_nan_boxed(self.0)
}
@@ -126,6 +149,10 @@ impl JsValue {
is_nan_boxed(self.0) && get_tag(self.0) == TAG_OBJECT
}
pub fn is_string(&self) -> bool {
is_nan_boxed(self.0) && get_tag(self.0) == TAG_STRING
}
pub fn as_double(&self) -> f64 {
debug_assert!(self.is_double(), "JsValue is not a double");
f64::from_bits(self.0)
@@ -145,6 +172,21 @@ impl JsValue {
debug_assert!(self.is_object(), "JsValue is not an object");
(self.0 & PTR_MASK) as *const u8
}
pub fn as_object_ptr(&self) -> *mut u8 {
debug_assert!(self.is_object(), "JsValue is not an object");
(self.0 & PTR_MASK) as *mut u8
}
pub fn as_string_index(&self) -> u32 {
debug_assert!(self.is_string(), "JsValue is not a string");
(self.0 & STRING_INDEX_MASK) as u32
}
pub fn as_string_hash(&self) -> u16 {
debug_assert!(self.is_string(), "JsValue is not a string");
((self.0 & STRING_HASH_MASK) >> STRING_HASH_SHIFT) as u16
}
}
fn is_nan_bits(bits: u64) -> bool {
@@ -152,7 +194,7 @@ fn is_nan_bits(bits: u64) -> bool {
}
fn is_nan_boxed(bits: u64) -> bool {
(0xFFF8..=0xFFFC).contains(&((bits >> 48) as u16))
(0xFFF8..=0xFFFD).contains(&((bits >> 48) as u16))
}
impl fmt::Display for JsValue {
@@ -180,6 +222,8 @@ impl fmt::Display for JsValue {
write!(f, "undefined")
} else if self.is_object() {
write!(f, "{{object}}")
} else if self.is_string() {
write!(f, "{{string}}")
} else {
write!(f, "{{unknown}}")
}
@@ -205,6 +249,13 @@ impl fmt::Debug for JsValue {
write!(f, "JsValue(Undefined)")
} else if self.is_object() {
write!(f, "JsValue(Object({:p}))", self.as_ptr())
} else if self.is_string() {
write!(
f,
"JsValue(String(idx={}, hash={:#06x}))",
self.as_string_index(),
self.as_string_hash()
)
} else {
write!(f, "JsValue(Unknown)")
}
@@ -271,6 +322,7 @@ mod tests {
val.is_null(),
val.is_undefined(),
val.is_object(),
val.is_string(),
];
let count = matched.iter().filter(|&&x| x).count();
assert_eq!(count, 1, "bits={bits:#018x} matched {count} types");