diff --git a/src/managed.rs b/src/managed.rs index b8440bb..ad7b6e7 100644 --- a/src/managed.rs +++ b/src/managed.rs @@ -5,6 +5,17 @@ use crate::type_info::TypeInfo; use crate::{constants, MonoReader}; use std::cmp; +/// A float as JSON text. `f32::to_string` is valid JSON for finite values but +/// emits `NaN` / `inf` otherwise, which breaks the whole document. +pub fn json_number>(v: F) -> String { + let v: f64 = v.into(); + if v.is_finite() { + v.to_string() + } else { + "null".to_string() + } +} + pub struct Managed<'a> { reader: &'a MonoReader, pub addr: usize, @@ -37,12 +48,12 @@ impl<'a> Managed<'a> { self.reader.read_u32(self.addr) } - pub fn read_r4(&self) -> i32 { - self.reader.read_i32(self.addr) + pub fn read_r4(&self) -> f32 { + self.reader.read_f32(self.addr) } - pub fn read_r8(&self) -> i64 { - self.reader.read_i64(self.addr) + pub fn read_r8(&self) -> f64 { + self.reader.read_f64(self.addr) } // read_i @@ -208,8 +219,8 @@ impl<'a> Managed<'a> { let var = match gen_type.clone().code() { TypeCode::I4 => managed_var.read_i4().to_string(), TypeCode::U4 => managed_var.read_u4().to_string(), - TypeCode::R4 => managed_var.read_r4().to_string(), - TypeCode::R8 => managed_var.read_r8().to_string(), + TypeCode::R4 => json_number(managed_var.read_r4()), + TypeCode::R8 => json_number(managed_var.read_r8()), TypeCode::I => managed_var.read_i4().to_string(), TypeCode::U => managed_var.read_u4().to_string(), TypeCode::I2 => managed_var.read_i2().to_string(), @@ -287,3 +298,23 @@ fn get_type_size(type_code: TypeCode) -> usize { _ => 0, } } + +#[cfg(test)] +mod tests { + use super::json_number; + + #[test] + fn finite_floats_are_json_numbers() { + // The raw bits the old reader emitted as the integer 1119965110. + assert_eq!(json_number(f32::from_bits(0x42c1_4fb6)), "96.65568542480469"); + assert_eq!(json_number(0.0_f32), "0"); + assert_eq!(json_number(-1.5_f64), "-1.5"); + } + + #[test] + fn non_finite_floats_become_null() { + assert_eq!(json_number(f32::NAN), "null"); + assert_eq!(json_number(f64::INFINITY), "null"); + assert_eq!(json_number(f32::NEG_INFINITY), "null"); + } +} diff --git a/src/napi/mod.rs b/src/napi/mod.rs index c88de92..04ab129 100644 --- a/src/napi/mod.rs +++ b/src/napi/mod.rs @@ -609,6 +609,8 @@ mod windows_backend { TypeCode::U4 => "System.UInt32".to_string(), TypeCode::I8 => "System.Int64".to_string(), TypeCode::U8 => "System.UInt64".to_string(), + TypeCode::R4 => "System.Single".to_string(), + TypeCode::R8 => "System.Double".to_string(), TypeCode::BOOLEAN => "System.Boolean".to_string(), TypeCode::STRING => "System.String".to_string(), _ => format!("TypeCode({})", field.type_info.type_code), @@ -631,6 +633,8 @@ mod windows_backend { TypeCode::U4 => "System.UInt32".to_string(), TypeCode::I8 => "System.Int64".to_string(), TypeCode::U8 => "System.UInt64".to_string(), + TypeCode::R4 => "System.Single".to_string(), + TypeCode::R8 => "System.Double".to_string(), TypeCode::BOOLEAN => "System.Boolean".to_string(), TypeCode::STRING => "System.String".to_string(), TypeCode::OBJECT => "System.Object".to_string(), diff --git a/src/type_definition.rs b/src/type_definition.rs index 32724aa..a2f3e5d 100644 --- a/src/type_definition.rs +++ b/src/type_definition.rs @@ -1,5 +1,5 @@ use crate::field_definition::FieldDefinition; -use crate::managed::Managed; +use crate::managed::{json_number, Managed}; use crate::mono_class_kind::{match_class_kind, MonoClassKind}; use crate::{constants, MonoReader, TypeCode, TypeInfo}; @@ -260,8 +260,8 @@ impl fmt::Display for TypeDefinition<'_> { TypeCode::BOOLEAN => managed.read_boolean().to_string(), TypeCode::U4 => managed.read_u4().to_string(), TypeCode::U => managed.read_u4().to_string(), - TypeCode::R4 => managed.read_r4().to_string(), - TypeCode::R8 => managed.read_r8().to_string(), + TypeCode::R4 => json_number(managed.read_r4()), + TypeCode::R8 => json_number(managed.read_r8()), TypeCode::I4 => managed.read_i4().to_string(), TypeCode::I => managed.read_i4().to_string(), TypeCode::I2 => managed.read_i2().to_string(),