diff --git a/spec/std/float_printer/ryu_printf_spec.cr b/spec/std/float_printer/ryu_printf_spec.cr index f8e80d1a33dd..a175d826eb99 100644 --- a/spec/std/float_printer/ryu_printf_spec.cr +++ b/spec/std/float_printer/ryu_printf_spec.cr @@ -1,3 +1,5 @@ +{% skip_file if flag?(:wasm32) %} + # This file contains test cases derived from: # # * https://github.com/ulfjack/ryu diff --git a/src/float/printer/ryu_printf.cr b/src/float/printer/ryu_printf.cr index d14ac70a831c..40b9ddb2c1d5 100644 --- a/src/float/printer/ryu_printf.cr +++ b/src/float/printer/ryu_printf.cr @@ -1,3 +1,5 @@ +{% skip_file if flag?(:wasm32) %} + require "./ryu_printf_table" # Source port of Ryu Printf's reference implementation in C. diff --git a/src/float/printer/ryu_printf_table.cr b/src/float/printer/ryu_printf_table.cr index c24d75a048f2..c8e78f6e49e0 100644 --- a/src/float/printer/ryu_printf_table.cr +++ b/src/float/printer/ryu_printf_table.cr @@ -1,3 +1,5 @@ +{% skip_file if flag?(:wasm32) %} + module Float::Printer::RyuPrintf {% begin %} # A table of all two-digit numbers. This is used to speed up decimal digit diff --git a/src/string/formatter.cr b/src/string/formatter.cr index e39ecbaa87a6..cdf5710b45f4 100644 --- a/src/string/formatter.cr +++ b/src/string/formatter.cr @@ -307,18 +307,25 @@ struct String::Formatter(A) elsif float.nan? float_special("nan", 1, flags) else - case flags.type - when 'f' - float_fixed(float, flags) - when 'e', 'E' - float_scientific(float, flags) - when 'g', 'G' - float_general(float, flags) - when 'a', 'A' - float_hex(float, flags) - else - raise "BUG: Unknown format type '#{flags.type}'" - end + # FIXME: wasm32 appears to run out of memory if we use Ryu Printf, which + # initializes very large lookup tables, so we always fall back to + # `LibC.snprintf` (#13918) + {% if flag?(:wasm32) %} + float_fallback(float, flags) + {% else %} + case flags.type + when 'f' + float_fixed(float, flags) + when 'e', 'E' + float_scientific(float, flags) + when 'g', 'G' + float_general(float, flags) + when 'a', 'A' + float_hex(float, flags) + else + raise "BUG: Unknown format type '#{flags.type}'" + end + {% end %} end else raise ArgumentError.new("Expected a float, not #{arg.inspect}") @@ -339,61 +346,63 @@ struct String::Formatter(A) pad(str_size, flags) if flags.right_padding? end - # Formats floats with `%f` - private def float_fixed(float, flags) - # the longest string possible is due to `Float64::MIN_SUBNORMAL`, which - # produces `0.` followed by 1074 nonzero digits; there is also no need - # for any precision > 1074 because all trailing digits will be zeros - if precision = flags.precision - printf_precision = {precision.to_u32, 1074_u32}.min - trailing_zeros = {precision - printf_precision, 0}.max - else - # default precision for C's `%f` - printf_precision = 6_u32 - trailing_zeros = 0 - end + {% unless flag?(:wasm32) %} + # Formats floats with `%f` + private def float_fixed(float, flags) + # the longest string possible is due to `Float64::MIN_SUBNORMAL`, which + # produces `0.` followed by 1074 nonzero digits; there is also no need + # for any precision > 1074 because all trailing digits will be zeros + if precision = flags.precision + printf_precision = {precision.to_u32, 1074_u32}.min + trailing_zeros = {precision - printf_precision, 0}.max + else + # default precision for C's `%f` + printf_precision = 6_u32 + trailing_zeros = 0 + end - printf_buf = uninitialized UInt8[1076] - printf_size = Float::Printer::RyuPrintf.d2fixed_buffered_n(float, printf_precision, printf_buf.to_unsafe) - printf_slice = printf_buf.to_slice[0, printf_size] - dot_index = printf_slice.index('.'.ord) - sign = Math.copysign(1.0, float) + printf_buf = uninitialized UInt8[1076] + printf_size = Float::Printer::RyuPrintf.d2fixed_buffered_n(float, printf_precision, printf_buf.to_unsafe) + printf_slice = printf_buf.to_slice[0, printf_size] + dot_index = printf_slice.index('.'.ord) + sign = Math.copysign(1.0, float) - str_size = printf_size + trailing_zeros - str_size += 1 if sign < 0 || flags.plus || flags.space - str_size += 1 if flags.sharp && dot_index.nil? + str_size = printf_size + trailing_zeros + str_size += 1 if sign < 0 || flags.plus || flags.space + str_size += 1 if flags.sharp && dot_index.nil? - pad(str_size, flags) if flags.left_padding? && flags.padding_char != '0' + pad(str_size, flags) if flags.left_padding? && flags.padding_char != '0' - # this preserves -0.0's sign correctly - write_plus_or_space(sign, flags) - @io << '-' if sign < 0 + # this preserves -0.0's sign correctly + write_plus_or_space(sign, flags) + @io << '-' if sign < 0 - pad(str_size, flags) if flags.left_padding? && flags.padding_char == '0' - @io.write_string(printf_slice) - trailing_zeros.times { @io << '0' } - @io << '.' if flags.sharp && dot_index.nil? + pad(str_size, flags) if flags.left_padding? && flags.padding_char == '0' + @io.write_string(printf_slice) + trailing_zeros.times { @io << '0' } + @io << '.' if flags.sharp && dot_index.nil? - pad(str_size, flags) if flags.right_padding? - end + pad(str_size, flags) if flags.right_padding? + end - # Formats floats with `%e` or `%E` - private def float_scientific(float, flags) - # TODO: implement using `Float::Printer::RyuPrintf` - float_fallback(float, flags) - end + # Formats floats with `%e` or `%E` + private def float_scientific(float, flags) + # TODO: implement using `Float::Printer::RyuPrintf` + float_fallback(float, flags) + end - # Formats floats with `%g` or `%G` - private def float_general(float, flags) - # TODO: implement using `Float::Printer::RyuPrintf` - float_fallback(float, flags) - end + # Formats floats with `%g` or `%G` + private def float_general(float, flags) + # TODO: implement using `Float::Printer::RyuPrintf` + float_fallback(float, flags) + end - # Formats floats with `%a` or `%A` - private def float_hex(float, flags) - # TODO: implement using `Float::Printer::Hexfloat` - float_fallback(float, flags) - end + # Formats floats with `%a` or `%A` + private def float_hex(float, flags) + # TODO: implement using `Float::Printer::Hexfloat` + float_fallback(float, flags) + end + {% end %} # Delegate to `LibC.snprintf` for float formats not yet ported to Crystal private def float_fallback(float, flags)