rubyduino 0.1.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +7 -0
- data/.gitmodules +3 -0
- data/CHANGELOG.md +9 -0
- data/LICENSE.txt +21 -0
- data/README.md +72 -0
- data/Rakefile +12 -0
- data/bin/rubyduino +186 -0
- data/examples/hello.rb +9 -0
- data/lib/rubyduino/arduino_entry.c +9 -0
- data/lib/rubyduino/sp_runtime.h +159 -0
- data/lib/rubyduino/spinel.rb +8 -0
- data/lib/rubyduino/spinel_arduino_codegen.rb +129 -0
- data/lib/rubyduino/version.rb +5 -0
- data/lib/rubyduino.rb +9 -0
- data/sig/rubyduino.rbs +4 -0
- data/vendor/spinel/AUTHORS +1 -0
- data/vendor/spinel/LICENSE +19 -0
- data/vendor/spinel/Makefile +406 -0
- data/vendor/spinel/POLY-AS-SET.md +217 -0
- data/vendor/spinel/README.md +409 -0
- data/vendor/spinel/benchmark/bm_ackermann.rb +12 -0
- data/vendor/spinel/benchmark/bm_ao_render.rb +323 -0
- data/vendor/spinel/benchmark/bm_attr_accessor.rb +41 -0
- data/vendor/spinel/benchmark/bm_bigint_fib.rb +10 -0
- data/vendor/spinel/benchmark/bm_binary_trees.rb +53 -0
- data/vendor/spinel/benchmark/bm_csv_process.rb +58 -0
- data/vendor/spinel/benchmark/bm_fannkuch.rb +88 -0
- data/vendor/spinel/benchmark/bm_fasta.rb +87 -0
- data/vendor/spinel/benchmark/bm_fib.rb +9 -0
- data/vendor/spinel/benchmark/bm_gcbench.rb +73 -0
- data/vendor/spinel/benchmark/bm_getivar.rb +34 -0
- data/vendor/spinel/benchmark/bm_getivar_module.rb +34 -0
- data/vendor/spinel/benchmark/bm_huffman.rb +141 -0
- data/vendor/spinel/benchmark/bm_inline.rb +68 -0
- data/vendor/spinel/benchmark/bm_io_wordcount.rb +57 -0
- data/vendor/spinel/benchmark/bm_json_parse.rb +157 -0
- data/vendor/spinel/benchmark/bm_keyword_args.rb +23 -0
- data/vendor/spinel/benchmark/bm_life.rb +70 -0
- data/vendor/spinel/benchmark/bm_linked_list.rb +75 -0
- data/vendor/spinel/benchmark/bm_loops_times.rb +16 -0
- data/vendor/spinel/benchmark/bm_mandel_term.rb +34 -0
- data/vendor/spinel/benchmark/bm_matmul.rb +42 -0
- data/vendor/spinel/benchmark/bm_nbody.rb +115 -0
- data/vendor/spinel/benchmark/bm_nested_loop.rb +29 -0
- data/vendor/spinel/benchmark/bm_nqueens.rb +46 -0
- data/vendor/spinel/benchmark/bm_object_new.rb +21 -0
- data/vendor/spinel/benchmark/bm_object_new_init.rb +21 -0
- data/vendor/spinel/benchmark/bm_object_new_no_escape.rb +28 -0
- data/vendor/spinel/benchmark/bm_partial_sums.rb +38 -0
- data/vendor/spinel/benchmark/bm_pidigits.rb +31 -0
- data/vendor/spinel/benchmark/bm_rbtree.rb +100 -0
- data/vendor/spinel/benchmark/bm_ruby_xor.rb +28 -0
- data/vendor/spinel/benchmark/bm_send_bmethod.rb +31 -0
- data/vendor/spinel/benchmark/bm_send_cfunc_block.rb +18 -0
- data/vendor/spinel/benchmark/bm_send_rubyfunc_block.rb +23 -0
- data/vendor/spinel/benchmark/bm_setivar.rb +33 -0
- data/vendor/spinel/benchmark/bm_setivar_object.rb +33 -0
- data/vendor/spinel/benchmark/bm_setivar_young.rb +37 -0
- data/vendor/spinel/benchmark/bm_sieve.rb +23 -0
- data/vendor/spinel/benchmark/bm_so_lists.rb +47 -0
- data/vendor/spinel/benchmark/bm_so_mandelbrot.rb +65 -0
- data/vendor/spinel/benchmark/bm_spectral_norm.rb +71 -0
- data/vendor/spinel/benchmark/bm_splay.rb +128 -0
- data/vendor/spinel/benchmark/bm_str_concat.rb +15 -0
- data/vendor/spinel/benchmark/bm_structaref.rb +25 -0
- data/vendor/spinel/benchmark/bm_structaset.rb +24 -0
- data/vendor/spinel/benchmark/bm_sudoku.rb +217 -0
- data/vendor/spinel/benchmark/bm_tak.rb +10 -0
- data/vendor/spinel/benchmark/bm_tarai.rb +10 -0
- data/vendor/spinel/benchmark/bm_template.rb +50 -0
- data/vendor/spinel/benchmark/bm_throw.rb +25 -0
- data/vendor/spinel/benchmark/bm_wordfreq.rb +43 -0
- data/vendor/spinel/docs/FFI.md +198 -0
- data/vendor/spinel/examples/ffi/libm/README.md +24 -0
- data/vendor/spinel/examples/ffi/libm/libm.rb +17 -0
- data/vendor/spinel/examples/ffi/sqlite/README.md +73 -0
- data/vendor/spinel/examples/ffi/sqlite/blog.rb +185 -0
- data/vendor/spinel/examples/ffi/sqlite/sqlite3_lib.rb +46 -0
- data/vendor/spinel/lib/erb.rb +19 -0
- data/vendor/spinel/lib/forwardable.rb +8 -0
- data/vendor/spinel/lib/mruby_shim.h +118 -0
- data/vendor/spinel/lib/optparse.rb +123 -0
- data/vendor/spinel/lib/regexp/re_compile.c +979 -0
- data/vendor/spinel/lib/regexp/re_exec.c +665 -0
- data/vendor/spinel/lib/regexp/re_internal.h +146 -0
- data/vendor/spinel/lib/regexp/re_utf8.c +76 -0
- data/vendor/spinel/lib/set.rb +12 -0
- data/vendor/spinel/lib/sp_bigint.c +5400 -0
- data/vendor/spinel/lib/sp_bigint.h +117 -0
- data/vendor/spinel/lib/sp_runtime.h +1361 -0
- data/vendor/spinel/lib/stringio.c +286 -0
- data/vendor/spinel/lib/stringio.rb +5 -0
- data/vendor/spinel/lib/strscan.c +170 -0
- data/vendor/spinel/lib/strscan.rb +55 -0
- data/vendor/spinel/spinel +191 -0
- data/vendor/spinel/spinel.bat +158 -0
- data/vendor/spinel/spinel_codegen.rb +39906 -0
- data/vendor/spinel/spinel_parse +0 -0
- data/vendor/spinel/spinel_parse.c +1586 -0
- data/vendor/spinel/test/alias_global.rb +16 -0
- data/vendor/spinel/test/alias_global.rb.expected +3 -0
- data/vendor/spinel/test/alias_method.rb +28 -0
- data/vendor/spinel/test/alias_method.rb.expected +5 -0
- data/vendor/spinel/test/and_node_poly_operand.rb +24 -0
- data/vendor/spinel/test/anon_block_forward.rb +47 -0
- data/vendor/spinel/test/anon_block_forward.rb.expected +4 -0
- data/vendor/spinel/test/array_3d_nested.rb +48 -0
- data/vendor/spinel/test/array_clear_typed.rb +40 -0
- data/vendor/spinel/test/array_clear_typed.rb.expected +7 -0
- data/vendor/spinel/test/array_concat.rb +18 -0
- data/vendor/spinel/test/array_concat.rb.expected +2 -0
- data/vendor/spinel/test/array_each_with_object.rb +17 -0
- data/vendor/spinel/test/array_each_with_object.rb.expected +2 -0
- data/vendor/spinel/test/array_fill_poly.rb +17 -0
- data/vendor/spinel/test/array_flat_map.rb +16 -0
- data/vendor/spinel/test/array_flat_map.rb.expected +2 -0
- data/vendor/spinel/test/array_keyed_hash_int_array_eql.rb +29 -0
- data/vendor/spinel/test/array_keyed_hash_int_array_eql.rb.expected +6 -0
- data/vendor/spinel/test/array_method_name_clash.rb +15 -0
- data/vendor/spinel/test/array_method_name_clash.rb.expected +1 -0
- data/vendor/spinel/test/array_new_block_typed_container.rb +37 -0
- data/vendor/spinel/test/array_new_block_typed_container.rb.expected +7 -0
- data/vendor/spinel/test/array_new_empty_inner_deferred.rb +47 -0
- data/vendor/spinel/test/array_new_empty_inner_deferred.rb.expected +8 -0
- data/vendor/spinel/test/array_partition_gc.rb +20 -0
- data/vendor/spinel/test/array_partition_gc.rb.expected +3 -0
- data/vendor/spinel/test/array_plus.rb +18 -0
- data/vendor/spinel/test/array_plus.rb.expected +2 -0
- data/vendor/spinel/test/array_rotate_bang.rb +54 -0
- data/vendor/spinel/test/array_rotate_bang.rb.expected +34 -0
- data/vendor/spinel/test/array_shuffle_ptr.rb +11 -0
- data/vendor/spinel/test/array_shuffle_ptr.rb.expected +1 -0
- data/vendor/spinel/test/array_slice_bang.rb +81 -0
- data/vendor/spinel/test/array_slice_bang.rb.expected +40 -0
- data/vendor/spinel/test/attr.rb +77 -0
- data/vendor/spinel/test/attr.rb.expected +9 -0
- data/vendor/spinel/test/attr_writer_poly_box.rb +24 -0
- data/vendor/spinel/test/attr_writer_poly_no_double_eval.rb +29 -0
- data/vendor/spinel/test/attr_writer_poly_no_double_eval.rb.expected +3 -0
- data/vendor/spinel/test/attr_writer_returns_rhs.rb +23 -0
- data/vendor/spinel/test/auto_unbox_keeps_int_slot.rb +38 -0
- data/vendor/spinel/test/auto_unbox_keeps_int_slot.rb.expected +3 -0
- data/vendor/spinel/test/auto_unbox_poly_to_int_local.rb +36 -0
- data/vendor/spinel/test/auto_unbox_poly_to_int_local.rb.expected +2 -0
- data/vendor/spinel/test/back_ref.rb +21 -0
- data/vendor/spinel/test/back_ref.rb.expected +6 -0
- data/vendor/spinel/test/bare_return_in_cls_method.rb +46 -0
- data/vendor/spinel/test/bare_return_in_cls_method.rb.expected +2 -0
- data/vendor/spinel/test/bare_return_in_initialize.rb +55 -0
- data/vendor/spinel/test/bare_return_in_initialize.rb.expected +4 -0
- data/vendor/spinel/test/bare_return_in_poly_method.rb +26 -0
- data/vendor/spinel/test/bare_return_in_poly_method.rb.expected +2 -0
- data/vendor/spinel/test/bigint_div_by_zero.rb +43 -0
- data/vendor/spinel/test/bigint_div_by_zero.rb.expected +3 -0
- data/vendor/spinel/test/block2.rb +31 -0
- data/vendor/spinel/test/block2.rb.expected +4 -0
- data/vendor/spinel/test/block_forward_block_arg.rb +60 -0
- data/vendor/spinel/test/block_forward_block_arg.rb.expected +4 -0
- data/vendor/spinel/test/block_forward_recv_typed.rb +82 -0
- data/vendor/spinel/test/block_forward_recv_typed.rb.expected +11 -0
- data/vendor/spinel/test/block_forward_self_call.rb +61 -0
- data/vendor/spinel/test/block_forward_self_call.rb.expected +6 -0
- data/vendor/spinel/test/block_given_block_param.rb +46 -0
- data/vendor/spinel/test/block_given_block_param.rb.expected +3 -0
- data/vendor/spinel/test/block_param_shadow.rb +246 -0
- data/vendor/spinel/test/block_param_shadow.rb.expected +71 -0
- data/vendor/spinel/test/bm_instance_eval.rb +209 -0
- data/vendor/spinel/test/bm_instance_eval.rb.expected +27 -0
- data/vendor/spinel/test/bound_method_array.rb +22 -0
- data/vendor/spinel/test/bound_method_array.rb.expected +4 -0
- data/vendor/spinel/test/bound_method_basic.rb +27 -0
- data/vendor/spinel/test/bound_method_basic.rb.expected +2 -0
- data/vendor/spinel/test/bound_method_gc.rb +30 -0
- data/vendor/spinel/test/bound_method_gc.rb.expected +1 -0
- data/vendor/spinel/test/bound_method_obj_recv.rb +41 -0
- data/vendor/spinel/test/bound_method_obj_recv.rb.expected +4 -0
- data/vendor/spinel/test/bound_method_single_eval.rb +37 -0
- data/vendor/spinel/test/bound_method_single_eval.rb.expected +4 -0
- data/vendor/spinel/test/box_pointer_to_poly.rb +27 -0
- data/vendor/spinel/test/box_pointer_to_poly.rb.expected +1 -0
- data/vendor/spinel/test/box_unbox_local_assign_cond.rb +22 -0
- data/vendor/spinel/test/box_unbox_local_assign_cond.rb.expected +2 -0
- data/vendor/spinel/test/bundle_array_a.rb +400 -0
- data/vendor/spinel/test/bundle_array_a.rb.expected +117 -0
- data/vendor/spinel/test/bundle_array_b.rb +369 -0
- data/vendor/spinel/test/bundle_array_b.rb.expected +136 -0
- data/vendor/spinel/test/bundle_array_c.rb +146 -0
- data/vendor/spinel/test/bundle_array_c.rb.expected +51 -0
- data/vendor/spinel/test/bundle_array_d.rb +181 -0
- data/vendor/spinel/test/bundle_array_d.rb.expected +66 -0
- data/vendor/spinel/test/bundle_hash.rb +328 -0
- data/vendor/spinel/test/bundle_hash.rb.expected +104 -0
- data/vendor/spinel/test/bundle_int_array.rb +125 -0
- data/vendor/spinel/test/bundle_int_array.rb.expected +24 -0
- data/vendor/spinel/test/bundle_integer.rb +248 -0
- data/vendor/spinel/test/bundle_integer.rb.expected +113 -0
- data/vendor/spinel/test/bundle_io_sys.rb +223 -0
- data/vendor/spinel/test/bundle_io_sys.rb.expected +62 -0
- data/vendor/spinel/test/bundle_misc_a.rb +462 -0
- data/vendor/spinel/test/bundle_misc_a.rb.expected +208 -0
- data/vendor/spinel/test/bundle_misc_b.rb +401 -0
- data/vendor/spinel/test/bundle_misc_b.rb.expected +120 -0
- data/vendor/spinel/test/bundle_poly.rb +145 -0
- data/vendor/spinel/test/bundle_poly.rb.expected +66 -0
- data/vendor/spinel/test/bundle_string_a.rb +278 -0
- data/vendor/spinel/test/bundle_string_a.rb.expected +93 -0
- data/vendor/spinel/test/bundle_string_b.rb +226 -0
- data/vendor/spinel/test/bundle_string_b.rb.expected +83 -0
- data/vendor/spinel/test/bundle_sym.rb +302 -0
- data/vendor/spinel/test/bundle_sym.rb.expected +117 -0
- data/vendor/spinel/test/call_and_write.rb +28 -0
- data/vendor/spinel/test/call_and_write.rb.expected +3 -0
- data/vendor/spinel/test/call_arg_int_to_obj_cast.rb +55 -0
- data/vendor/spinel/test/call_arg_int_to_obj_cast.rb.expected +1 -0
- data/vendor/spinel/test/call_op_write.rb +51 -0
- data/vendor/spinel/test/call_op_write.rb.expected +11 -0
- data/vendor/spinel/test/call_or_write.rb +35 -0
- data/vendor/spinel/test/call_or_write.rb.expected +3 -0
- data/vendor/spinel/test/case.rb +43 -0
- data/vendor/spinel/test/case.rb.expected +10 -0
- data/vendor/spinel/test/case_string_when_sym_no_match.rb +59 -0
- data/vendor/spinel/test/case_string_when_sym_no_match.rb.expected +7 -0
- data/vendor/spinel/test/case_when_class.rb +70 -0
- data/vendor/spinel/test/case_when_class.rb.expected +5 -0
- data/vendor/spinel/test/chained_attr_setter.rb +55 -0
- data/vendor/spinel/test/chained_ivar_op_assign_emits_inner_write.rb +28 -0
- data/vendor/spinel/test/chained_ivar_op_assign_emits_inner_write.rb.expected +3 -0
- data/vendor/spinel/test/chained_ivar_write_call_rhs.rb +30 -0
- data/vendor/spinel/test/chained_ivar_write_call_rhs.rb.expected +2 -0
- data/vendor/spinel/test/chained_ivar_write_split.rb +26 -0
- data/vendor/spinel/test/chained_ivar_write_split.rb.expected +3 -0
- data/vendor/spinel/test/chained_ivar_write_subclass.rb +26 -0
- data/vendor/spinel/test/chained_ivar_write_subclass.rb.expected +2 -0
- data/vendor/spinel/test/chained_or_assign_collection_array_nested.rb +32 -0
- data/vendor/spinel/test/chained_or_assign_collection_array_nested.rb.expected +3 -0
- data/vendor/spinel/test/chained_or_assign_collection_element.rb +29 -0
- data/vendor/spinel/test/chained_or_assign_collection_element.rb.expected +2 -0
- data/vendor/spinel/test/chained_or_assign_collection_poly_array.rb +25 -0
- data/vendor/spinel/test/chained_or_assign_collection_poly_array.rb.expected +3 -0
- data/vendor/spinel/test/chained_or_assign_collection_str_keys.rb +24 -0
- data/vendor/spinel/test/chained_or_assign_collection_str_keys.rb.expected +3 -0
- data/vendor/spinel/test/class_constant_path_read.rb +26 -0
- data/vendor/spinel/test/class_constant_path_read.rb.expected +4 -0
- data/vendor/spinel/test/class_constant_sym_array.rb +19 -0
- data/vendor/spinel/test/class_constant_sym_array.rb.expected +4 -0
- data/vendor/spinel/test/class_def_order.rb +66 -0
- data/vendor/spinel/test/class_def_order.rb.expected +4 -0
- data/vendor/spinel/test/class_method_open_class_call.rb +31 -0
- data/vendor/spinel/test/class_method_open_class_call.rb.expected +3 -0
- data/vendor/spinel/test/class_var_read.rb +113 -0
- data/vendor/spinel/test/class_var_read.rb.expected +8 -0
- data/vendor/spinel/test/class_var_write.rb +19 -0
- data/vendor/spinel/test/class_var_write.rb.expected +1 -0
- data/vendor/spinel/test/cls_ivar_type_parent_defer.rb +32 -0
- data/vendor/spinel/test/cls_method_object.rb +31 -0
- data/vendor/spinel/test/cls_method_object.rb.expected +2 -0
- data/vendor/spinel/test/codegen_class_in_module_with_explicit_parent.rb +15 -0
- data/vendor/spinel/test/codegen_class_in_module_with_explicit_parent.rb.expected +1 -0
- data/vendor/spinel/test/codegen_const_lookup_in_toplevel_method.rb +13 -0
- data/vendor/spinel/test/codegen_const_lookup_in_toplevel_method.rb.expected +1 -0
- data/vendor/spinel/test/comparable.rb +21 -0
- data/vendor/spinel/test/comparable.rb.expected +5 -0
- data/vendor/spinel/test/const_init_block_param_scan.rb +13 -0
- data/vendor/spinel/test/const_init_block_param_scan.rb.expected +2 -0
- data/vendor/spinel/test/constant_path.rb +102 -0
- data/vendor/spinel/test/constant_path.rb.expected +21 -0
- data/vendor/spinel/test/constants.rb +44 -0
- data/vendor/spinel/test/constants.rb.expected +13 -0
- data/vendor/spinel/test/control.rb +23 -0
- data/vendor/spinel/test/control.rb.expected +7 -0
- data/vendor/spinel/test/default_args.rb +108 -0
- data/vendor/spinel/test/default_args.rb.expected +13 -0
- data/vendor/spinel/test/default_argv_narrows_from_string_call_site.rb +32 -0
- data/vendor/spinel/test/default_argv_narrows_from_string_call_site.rb.expected +1 -0
- data/vendor/spinel/test/each_poly_recv_block_auto_splat.rb +43 -0
- data/vendor/spinel/test/each_poly_recv_block_auto_splat.rb.expected +2 -0
- data/vendor/spinel/test/each_with_object_seed_shadow.rb +14 -0
- data/vendor/spinel/test/each_with_object_seed_shadow.rb.expected +4 -0
- data/vendor/spinel/test/elsif_isa_chain.rb +56 -0
- data/vendor/spinel/test/elsif_isa_chain.rb.expected +6 -0
- data/vendor/spinel/test/embedded_var.rb +17 -0
- data/vendor/spinel/test/embedded_var.rb.expected +2 -0
- data/vendor/spinel/test/empty_array_param.rb +94 -0
- data/vendor/spinel/test/empty_array_param.rb.expected +18 -0
- data/vendor/spinel/test/empty_hash_ivar_string_value.rb +51 -0
- data/vendor/spinel/test/empty_hash_ivar_string_value.rb.expected +4 -0
- data/vendor/spinel/test/endless_method.rb +61 -0
- data/vendor/spinel/test/endless_method.rb.expected +11 -0
- data/vendor/spinel/test/endless_method_rescue.rb +33 -0
- data/vendor/spinel/test/endless_method_rescue.rb.expected +10 -0
- data/vendor/spinel/test/ensure_raise_overrides_body_raise.rb +19 -0
- data/vendor/spinel/test/ensure_raise_overrides_body_raise.rb.expected +1 -0
- data/vendor/spinel/test/ensure_runs_on_raise.rb +32 -0
- data/vendor/spinel/test/ensure_runs_on_raise.rb.expected +3 -0
- data/vendor/spinel/test/ensure_runs_on_raise_nested.rb +38 -0
- data/vendor/spinel/test/ensure_runs_on_raise_nested.rb.expected +4 -0
- data/vendor/spinel/test/ensure_runs_on_return.rb +61 -0
- data/vendor/spinel/test/ensure_runs_on_return.rb.expected +6 -0
- data/vendor/spinel/test/ensure_runs_on_return_from_rescue.rb +26 -0
- data/vendor/spinel/test/ensure_runs_on_return_from_rescue.rb.expected +2 -0
- data/vendor/spinel/test/ensure_runs_on_return_nested.rb +37 -0
- data/vendor/spinel/test/ensure_runs_on_return_nested.rb.expected +3 -0
- data/vendor/spinel/test/enumerable.rb +19 -0
- data/vendor/spinel/test/enumerable.rb.expected +2 -0
- data/vendor/spinel/test/env_string_inference.rb +37 -0
- data/vendor/spinel/test/env_string_inference.rb.expected +6 -0
- data/vendor/spinel/test/exception_object_methods.rb +99 -0
- data/vendor/spinel/test/exception_object_methods.rb.expected +15 -0
- data/vendor/spinel/test/exceptions.rb +34 -0
- data/vendor/spinel/test/exceptions.rb.expected +4 -0
- data/vendor/spinel/test/features.rb +32 -0
- data/vendor/spinel/test/features.rb.expected +9 -0
- data/vendor/spinel/test/ffi_buffer_reader.rb +23 -0
- data/vendor/spinel/test/ffi_buffer_reader.rb.expected +5 -0
- data/vendor/spinel/test/ffi_const.rb +12 -0
- data/vendor/spinel/test/ffi_const.rb.expected +5 -0
- data/vendor/spinel/test/ffi_libc_libm_basic.rb +16 -0
- data/vendor/spinel/test/ffi_libc_libm_basic.rb.expected +5 -0
- data/vendor/spinel/test/ffi_ptr_nil.rb +27 -0
- data/vendor/spinel/test/ffi_ptr_nil.rb.expected +2 -0
- data/vendor/spinel/test/ffi_void_return.rb +14 -0
- data/vendor/spinel/test/ffi_void_return.rb.expected +2 -0
- data/vendor/spinel/test/fiber_ivar_persists_across_yield.rb +22 -0
- data/vendor/spinel/test/fiber_ivar_persists_across_yield.rb.expected +3 -0
- data/vendor/spinel/test/fiber_yield_across_method_call.rb +28 -0
- data/vendor/spinel/test/fiber_yield_across_method_call.rb.expected +3 -0
- data/vendor/spinel/test/file_basename_gc.rb +59 -0
- data/vendor/spinel/test/file_basename_gc.rb.expected +2 -0
- data/vendor/spinel/test/forward_call_class_method_inherited_init.rb +36 -0
- data/vendor/spinel/test/forward_call_class_method_inherited_init.rb.expected +1 -0
- data/vendor/spinel/test/forward_call_class_method_inherited_init_int_array.rb +32 -0
- data/vendor/spinel/test/forward_call_class_method_inherited_init_int_array.rb.expected +1 -0
- data/vendor/spinel/test/forward_call_class_method_inherited_init_obj.rb +41 -0
- data/vendor/spinel/test/forward_call_class_method_inherited_init_obj.rb.expected +1 -0
- data/vendor/spinel/test/forward_call_class_method_int_array.rb +26 -0
- data/vendor/spinel/test/forward_call_class_method_int_array.rb.expected +1 -0
- data/vendor/spinel/test/forward_call_class_method_nested.rb +35 -0
- data/vendor/spinel/test/forward_call_class_method_nested.rb.expected +1 -0
- data/vendor/spinel/test/forward_call_class_method_obj.rb +36 -0
- data/vendor/spinel/test/forward_call_class_method_obj.rb.expected +1 -0
- data/vendor/spinel/test/forward_call_param_type_inference.rb +23 -0
- data/vendor/spinel/test/forward_call_param_type_inference.rb.expected +1 -0
- data/vendor/spinel/test/forward_call_param_type_int_array.rb +34 -0
- data/vendor/spinel/test/forward_call_param_type_int_array.rb.expected +1 -0
- data/vendor/spinel/test/forward_call_param_type_obj.rb +38 -0
- data/vendor/spinel/test/forward_call_param_type_obj.rb.expected +1 -0
- data/vendor/spinel/test/gc_root_ptr_array_literal.rb +63 -0
- data/vendor/spinel/test/gc_root_ptr_array_literal.rb.expected +1 -0
- data/vendor/spinel/test/gc_save_double_in_new.rb +13 -0
- data/vendor/spinel/test/gc_save_double_in_new.rb.expected +1 -0
- data/vendor/spinel/test/gc_scan_skip_inherited_ivar.rb +54 -0
- data/vendor/spinel/test/gc_scan_skip_inherited_ivar.rb.expected +2 -0
- data/vendor/spinel/test/global_var.rb +53 -0
- data/vendor/spinel/test/global_var.rb.expected +12 -0
- data/vendor/spinel/test/gvar.rb +22 -0
- data/vendor/spinel/test/gvar.rb.expected +4 -0
- data/vendor/spinel/test/hash_dig.rb +92 -0
- data/vendor/spinel/test/hash_dig.rb.expected +37 -0
- data/vendor/spinel/test/hash_keys_each_shadow.rb +40 -0
- data/vendor/spinel/test/hash_keys_each_shadow.rb.expected +9 -0
- data/vendor/spinel/test/hash_shorthand.rb +37 -0
- data/vendor/spinel/test/hash_shorthand.rb.expected +6 -0
- data/vendor/spinel/test/hash_shorthand_str.rb +48 -0
- data/vendor/spinel/test/hash_shorthand_str.rb.expected +19 -0
- data/vendor/spinel/test/heterogeneous_dispatch_int_return_narrow.rb +46 -0
- data/vendor/spinel/test/heterogeneous_dispatch_int_return_narrow.rb.expected +2 -0
- data/vendor/spinel/test/if_static_false_skips_dead_branch_compile.rb +50 -0
- data/vendor/spinel/test/if_static_false_skips_dead_branch_compile.rb.expected +3 -0
- data/vendor/spinel/test/implicit_self_param_poly.rb +17 -0
- data/vendor/spinel/test/implicit_self_param_poly.rb.expected +1 -0
- data/vendor/spinel/test/index_write.rb +106 -0
- data/vendor/spinel/test/index_write.rb.expected +30 -0
- data/vendor/spinel/test/inference_method_set.rb +35 -0
- data/vendor/spinel/test/inference_method_set.rb.expected +2 -0
- data/vendor/spinel/test/inherit.rb +52 -0
- data/vendor/spinel/test/inherit.rb.expected +7 -0
- data/vendor/spinel/test/inherited_method_param_widen.rb +49 -0
- data/vendor/spinel/test/inherited_method_param_widen.rb.expected +1 -0
- data/vendor/spinel/test/init_locals.rb +43 -0
- data/vendor/spinel/test/init_locals.rb.expected +3 -0
- data/vendor/spinel/test/initialize_param_poly_call_sites.rb +20 -0
- data/vendor/spinel/test/initialize_param_poly_call_sites.rb.expected +2 -0
- data/vendor/spinel/test/initialize_param_through_call.rb +15 -0
- data/vendor/spinel/test/initialize_param_through_call.rb.expected +1 -0
- data/vendor/spinel/test/initialize_void_wrapper_gc_save.rb +40 -0
- data/vendor/spinel/test/initialize_void_wrapper_gc_save.rb.expected +1 -0
- data/vendor/spinel/test/inspect.rb +86 -0
- data/vendor/spinel/test/inspect.rb.expected +52 -0
- data/vendor/spinel/test/instance_eval_trampoline.rb +88 -0
- data/vendor/spinel/test/instance_eval_trampoline.rb.expected +5 -0
- data/vendor/spinel/test/int_bracket_skip_sym_idx.rb +40 -0
- data/vendor/spinel/test/int_bracket_skip_sym_idx.rb.expected +5 -0
- data/vendor/spinel/test/int_keyed_hash_lookup_as_array.rb +19 -0
- data/vendor/spinel/test/int_keyed_hash_lookup_as_array.rb.expected +2 -0
- data/vendor/spinel/test/intarray_slice_assign_from_intarray_ptr_array_first.rb +25 -0
- data/vendor/spinel/test/intarray_slice_assign_from_intarray_ptr_array_first.rb.expected +1 -0
- data/vendor/spinel/test/integer_div.rb +21 -0
- data/vendor/spinel/test/integer_div.rb.expected +10 -0
- data/vendor/spinel/test/integer_div_by_zero.rb +80 -0
- data/vendor/spinel/test/integer_div_by_zero.rb.expected +10 -0
- data/vendor/spinel/test/interp_method_widening.rb +39 -0
- data/vendor/spinel/test/interp_method_widening.rb.expected +4 -0
- data/vendor/spinel/test/interp_symbol.rb +18 -0
- data/vendor/spinel/test/interp_symbol.rb.expected +3 -0
- data/vendor/spinel/test/introspect.rb +35 -0
- data/vendor/spinel/test/introspect.rb.expected +11 -0
- data/vendor/spinel/test/issue176_empty_hash_default.rb +34 -0
- data/vendor/spinel/test/issue176_empty_hash_default.rb.expected +3 -0
- data/vendor/spinel/test/issue203_string_new.rb +27 -0
- data/vendor/spinel/test/issue203_string_new.rb.expected +4 -0
- data/vendor/spinel/test/issue204_user_find_fetch.rb +25 -0
- data/vendor/spinel/test/issue204_user_find_fetch.rb.expected +5 -0
- data/vendor/spinel/test/issue207_factory_pattern.rb +35 -0
- data/vendor/spinel/test/issue207_factory_pattern.rb.expected +1 -0
- data/vendor/spinel/test/issue207_full_repro.rb +52 -0
- data/vendor/spinel/test/issue207_full_repro.rb.expected +1 -0
- data/vendor/spinel/test/issue208_inherited_class_method.rb +24 -0
- data/vendor/spinel/test/issue208_inherited_class_method.rb.expected +4 -0
- data/vendor/spinel/test/issue219_aref_chain.rb +36 -0
- data/vendor/spinel/test/issue219_aref_chain.rb.expected +5 -0
- data/vendor/spinel/test/issue224_bare_new_inherited.rb +66 -0
- data/vendor/spinel/test/issue224_bare_new_inherited.rb.expected +4 -0
- data/vendor/spinel/test/issue229_uncalled_cls_method.rb +76 -0
- data/vendor/spinel/test/issue229_uncalled_cls_method.rb.expected +3 -0
- data/vendor/spinel/test/issue235_chain_tail_widening.rb +48 -0
- data/vendor/spinel/test/issue235_chain_tail_widening.rb.expected +5 -0
- data/vendor/spinel/test/issue236_chain_empty_literal.rb +84 -0
- data/vendor/spinel/test/issue236_chain_empty_literal.rb.expected +7 -0
- data/vendor/spinel/test/issue239_cls_method_default_args.rb +41 -0
- data/vendor/spinel/test/issue239_cls_method_default_args.rb.expected +5 -0
- data/vendor/spinel/test/issue_266_def_length_inference.rb +63 -0
- data/vendor/spinel/test/issue_266_def_length_inference.rb.expected +1 -0
- data/vendor/spinel/test/ivar_and_write_basic.rb +34 -0
- data/vendor/spinel/test/ivar_and_write_basic.rb.expected +5 -0
- data/vendor/spinel/test/ivar_c_keyword.rb +14 -0
- data/vendor/spinel/test/ivar_c_keyword.rb.expected +1 -0
- data/vendor/spinel/test/ivar_float_array.rb +59 -0
- data/vendor/spinel/test/ivar_float_array.rb.expected +11 -0
- data/vendor/spinel/test/ivar_op_assign_bitwise_and_shift.rb +22 -0
- data/vendor/spinel/test/ivar_op_assign_bitwise_and_shift.rb.expected +6 -0
- data/vendor/spinel/test/ivar_or_write_basic.rb +57 -0
- data/vendor/spinel/test/ivar_or_write_basic.rb.expected +9 -0
- data/vendor/spinel/test/ivar_polymorphic.rb +87 -0
- data/vendor/spinel/test/ivar_polymorphic.rb.expected +12 -0
- data/vendor/spinel/test/ivar_ternary_mixed.rb +108 -0
- data/vendor/spinel/test/ivar_ternary_mixed.rb.expected +15 -0
- data/vendor/spinel/test/ivar_writer_heterogeneity.rb +34 -0
- data/vendor/spinel/test/ivar_writer_heterogeneity.rb.expected +2 -0
- data/vendor/spinel/test/known_constants.rb +25 -0
- data/vendor/spinel/test/known_constants.rb.expected +4 -0
- data/vendor/spinel/test/kw_nil_default_string_call.rb +6 -0
- data/vendor/spinel/test/kw_nil_default_string_call.rb.expected +2 -0
- data/vendor/spinel/test/kwargs.rb +21 -0
- data/vendor/spinel/test/kwargs.rb.expected +6 -0
- data/vendor/spinel/test/kwargs_param.rb +26 -0
- data/vendor/spinel/test/kwargs_param.rb.expected +2 -0
- data/vendor/spinel/test/kwargs_param_widen.rb +73 -0
- data/vendor/spinel/test/kwargs_param_widen.rb.expected +3 -0
- data/vendor/spinel/test/lrama_features.rb +142 -0
- data/vendor/spinel/test/lrama_features.rb.expected +38 -0
- data/vendor/spinel/test/map_array_block_result.rb +29 -0
- data/vendor/spinel/test/map_array_block_result.rb.expected +2 -0
- data/vendor/spinel/test/map_block_returns_nested_array.rb +53 -0
- data/vendor/spinel/test/map_empty_block.rb +26 -0
- data/vendor/spinel/test/map_empty_block.rb.expected +6 -0
- data/vendor/spinel/test/map_range_recv_array_block.rb +31 -0
- data/vendor/spinel/test/mega.rb +35 -0
- data/vendor/spinel/test/mega.rb.expected +7 -0
- data/vendor/spinel/test/method.rb +21 -0
- data/vendor/spinel/test/method.rb.expected +6 -0
- data/vendor/spinel/test/method_defined.rb +36 -0
- data/vendor/spinel/test/method_defined.rb.expected +17 -0
- data/vendor/spinel/test/method_dispatch_poly_array.rb +35 -0
- data/vendor/spinel/test/method_dispatch_poly_array.rb.expected +2 -0
- data/vendor/spinel/test/method_introspection.rb +24 -0
- data/vendor/spinel/test/method_introspection.rb.expected +6 -0
- data/vendor/spinel/test/method_param_unify_to_poly.rb +40 -0
- data/vendor/spinel/test/method_param_unify_to_poly.rb.expected +2 -0
- data/vendor/spinel/test/misc.rb +49 -0
- data/vendor/spinel/test/misc.rb.expected +7 -0
- data/vendor/spinel/test/mixin.rb +54 -0
- data/vendor/spinel/test/mixin.rb.expected +4 -0
- data/vendor/spinel/test/module_acc_dispatch_param_box.rb +46 -0
- data/vendor/spinel/test/module_acc_dispatch_param_box.rb.expected +2 -0
- data/vendor/spinel/test/module_class_new.rb +17 -0
- data/vendor/spinel/test/module_class_new.rb.expected +3 -0
- data/vendor/spinel/test/module_cls_method_string_return.rb +53 -0
- data/vendor/spinel/test/module_cls_method_string_return.rb.expected +5 -0
- data/vendor/spinel/test/module_const_array_widen.rb +36 -0
- data/vendor/spinel/test/module_const_array_widen.rb.expected +1 -0
- data/vendor/spinel/test/module_dispatch_param_widen.rb +55 -0
- data/vendor/spinel/test/module_dispatch_param_widen.rb.expected +1 -0
- data/vendor/spinel/test/module_function_namespace.rb +58 -0
- data/vendor/spinel/test/module_ivar_hash.rb +61 -0
- data/vendor/spinel/test/module_ivar_hash.rb.expected +7 -0
- data/vendor/spinel/test/module_relative_constant_path.rb +26 -0
- data/vendor/spinel/test/module_relative_constant_path.rb.expected +2 -0
- data/vendor/spinel/test/module_singleton_accessor.rb +65 -0
- data/vendor/spinel/test/module_singleton_accessor.rb.expected +3 -0
- data/vendor/spinel/test/module_singleton_accessor_poly.rb +62 -0
- data/vendor/spinel/test/module_singleton_accessor_poly.rb.expected +7 -0
- data/vendor/spinel/test/multi_arg_block_call.rb +56 -0
- data/vendor/spinel/test/multi_arg_block_call.rb.expected +5 -0
- data/vendor/spinel/test/multi_arg_yield.rb +70 -0
- data/vendor/spinel/test/multi_arg_yield.rb.expected +6 -0
- data/vendor/spinel/test/multi_assign_ivar.rb +33 -0
- data/vendor/spinel/test/multi_assign_ivar.rb.expected +1 -0
- data/vendor/spinel/test/multi_return.rb +35 -0
- data/vendor/spinel/test/multi_return.rb.expected +10 -0
- data/vendor/spinel/test/multi_return_bare.rb +77 -0
- data/vendor/spinel/test/multi_return_bare.rb.expected +16 -0
- data/vendor/spinel/test/multi_write_const.rb +36 -0
- data/vendor/spinel/test/multi_write_const.rb.expected +9 -0
- data/vendor/spinel/test/multi_write_from_poly_recv.rb +34 -0
- data/vendor/spinel/test/multi_write_from_poly_recv.rb.expected +1 -0
- data/vendor/spinel/test/multi_write_ivar.rb +34 -0
- data/vendor/spinel/test/multi_write_ivar.rb.expected +3 -0
- data/vendor/spinel/test/multi_write_ivar_const_from_array_rhs.rb +53 -0
- data/vendor/spinel/test/multi_write_ivar_const_from_array_rhs.rb.expected +6 -0
- data/vendor/spinel/test/multi_write_ivar_widening.rb +10 -0
- data/vendor/spinel/test/multi_write_ivar_widening.rb.expected +2 -0
- data/vendor/spinel/test/multi_write_map_block.rb +31 -0
- data/vendor/spinel/test/multi_write_map_block.rb.expected +7 -0
- data/vendor/spinel/test/multi_write_setter.rb +28 -0
- data/vendor/spinel/test/multi_write_setter.rb.expected +3 -0
- data/vendor/spinel/test/multi_write_typed_array_dispatch.rb +44 -0
- data/vendor/spinel/test/multi_write_typed_array_dispatch.rb.expected +3 -0
- data/vendor/spinel/test/nested_class_in_class.rb +38 -0
- data/vendor/spinel/test/nested_class_in_class.rb.expected +3 -0
- data/vendor/spinel/test/nested_class_module_const_resolution.rb +38 -0
- data/vendor/spinel/test/nested_class_module_const_resolution.rb.expected +2 -0
- data/vendor/spinel/test/nil_ivar.rb +44 -0
- data/vendor/spinel/test/nil_ivar.rb.expected +5 -0
- data/vendor/spinel/test/no_attr_write_shortcut_complex.rb +33 -0
- data/vendor/spinel/test/no_attr_write_shortcut_complex.rb.expected +3 -0
- data/vendor/spinel/test/no_attr_write_shortcut_multi.rb +38 -0
- data/vendor/spinel/test/no_attr_write_shortcut_multi.rb.expected +4 -0
- data/vendor/spinel/test/no_keywords_param.rb +15 -0
- data/vendor/spinel/test/no_keywords_param.rb.expected +2 -0
- data/vendor/spinel/test/nullable.rb +26 -0
- data/vendor/spinel/test/nullable.rb.expected +2 -0
- data/vendor/spinel/test/obj_array.rb +26 -0
- data/vendor/spinel/test/obj_array.rb.expected +6 -0
- data/vendor/spinel/test/object_new_sentinel.rb +18 -0
- data/vendor/spinel/test/object_new_sentinel.rb.expected +3 -0
- data/vendor/spinel/test/object_truthy.rb +31 -0
- data/vendor/spinel/test/object_truthy.rb.expected +5 -0
- data/vendor/spinel/test/op_assign_user_operator.rb +36 -0
- data/vendor/spinel/test/open_class.rb +27 -0
- data/vendor/spinel/test/open_class.rb.expected +4 -0
- data/vendor/spinel/test/optional_string_param_method.rb +11 -0
- data/vendor/spinel/test/optional_string_param_method.rb.expected +3 -0
- data/vendor/spinel/test/param_inference_if_predicate.rb +90 -0
- data/vendor/spinel/test/param_inference_if_predicate.rb.expected +3 -0
- data/vendor/spinel/test/param_narrow_from_body_call.rb +34 -0
- data/vendor/spinel/test/param_narrow_from_body_call.rb.expected +2 -0
- data/vendor/spinel/test/param_widens_when_body_assigns_string.rb +21 -0
- data/vendor/spinel/test/param_widens_when_body_assigns_string.rb.expected +6 -0
- data/vendor/spinel/test/pattern.rb +41 -0
- data/vendor/spinel/test/pattern.rb.expected +9 -0
- data/vendor/spinel/test/poly.rb +24 -0
- data/vendor/spinel/test/poly.rb.expected +9 -0
- data/vendor/spinel/test/poly2.rb +34 -0
- data/vendor/spinel/test/poly2.rb.expected +9 -0
- data/vendor/spinel/test/poly_arith.rb +34 -0
- data/vendor/spinel/test/poly_arith.rb.expected +10 -0
- data/vendor/spinel/test/poly_array_literal_callnode_typed_elem.rb +37 -0
- data/vendor/spinel/test/poly_array_literal_callnode_typed_elem.rb.expected +9 -0
- data/vendor/spinel/test/poly_array_slot_literal.rb +41 -0
- data/vendor/spinel/test/poly_array_slot_sized_default.rb +33 -0
- data/vendor/spinel/test/poly_box_int_array.rb +13 -0
- data/vendor/spinel/test/poly_box_int_array.rb.expected +2 -0
- data/vendor/spinel/test/poly_dispatch_args_ret.rb +45 -0
- data/vendor/spinel/test/poly_dispatch_args_ret.rb.expected +4 -0
- data/vendor/spinel/test/poly_dispatch_arity_padding.rb +36 -0
- data/vendor/spinel/test/poly_dispatch_arity_truncate.rb +45 -0
- data/vendor/spinel/test/poly_dispatch_attr_reader.rb +44 -0
- data/vendor/spinel/test/poly_dispatch_attr_reader.rb.expected +4 -0
- data/vendor/spinel/test/poly_dispatch_builtin.rb +16 -0
- data/vendor/spinel/test/poly_dispatch_builtin.rb.expected +2 -0
- data/vendor/spinel/test/poly_dispatch_builtin_all.rb +34 -0
- data/vendor/spinel/test/poly_dispatch_builtin_all.rb.expected +8 -0
- data/vendor/spinel/test/poly_dispatch_ptr_array.rb +57 -0
- data/vendor/spinel/test/poly_dispatch_ptr_array.rb.expected +6 -0
- data/vendor/spinel/test/poly_equality_mixed_sites.rb +40 -0
- data/vendor/spinel/test/poly_equality_mixed_sites.rb.expected +10 -0
- data/vendor/spinel/test/poly_gc.rb +39 -0
- data/vendor/spinel/test/poly_gc.rb.expected +3 -0
- data/vendor/spinel/test/poly_hash_literal_from_ivar.rb +47 -0
- data/vendor/spinel/test/poly_hash_literal_from_ivar.rb.expected +5 -0
- data/vendor/spinel/test/poly_hash_with_range.rb +36 -0
- data/vendor/spinel/test/poly_hash_with_range.rb.expected +10 -0
- data/vendor/spinel/test/poly_hash_with_time.rb +32 -0
- data/vendor/spinel/test/poly_hash_with_time.rb.expected +8 -0
- data/vendor/spinel/test/poly_int_arith_auto_unify.rb +26 -0
- data/vendor/spinel/test/poly_int_arith_auto_unify.rb.expected +4 -0
- data/vendor/spinel/test/poly_int_bit_index.rb +50 -0
- data/vendor/spinel/test/poly_int_bit_index.rb.expected +14 -0
- data/vendor/spinel/test/poly_int_bitops_auto_unify.rb +20 -0
- data/vendor/spinel/test/poly_int_bitops_auto_unify.rb.expected +5 -0
- data/vendor/spinel/test/poly_is_a.rb +47 -0
- data/vendor/spinel/test/poly_is_a.rb.expected +10 -0
- data/vendor/spinel/test/poly_keyed_hash_method_dedup.rb +37 -0
- data/vendor/spinel/test/poly_keyed_hash_method_dedup.rb.expected +4 -0
- data/vendor/spinel/test/poly_keyed_hash_pipeline.rb +40 -0
- data/vendor/spinel/test/poly_keyed_hash_pipeline.rb.expected +2 -0
- data/vendor/spinel/test/poly_method_args.rb +23 -0
- data/vendor/spinel/test/poly_method_args.rb.expected +2 -0
- data/vendor/spinel/test/poly_method_mixed_return.rb +23 -0
- data/vendor/spinel/test/poly_method_mixed_return.rb.expected +2 -0
- data/vendor/spinel/test/poly_or_returns_value.rb +13 -0
- data/vendor/spinel/test/poly_or_returns_value.rb.expected +4 -0
- data/vendor/spinel/test/poly_recv_aref_box_arm.rb +41 -0
- data/vendor/spinel/test/poly_recv_aref_box_arm.rb.expected +4 -0
- data/vendor/spinel/test/poly_recv_aref_str_key.rb +42 -0
- data/vendor/spinel/test/poly_recv_aref_str_key.rb.expected +2 -0
- data/vendor/spinel/test/poly_recv_bracket_assign.rb +31 -0
- data/vendor/spinel/test/poly_recv_bracket_assign.rb.expected +1 -0
- data/vendor/spinel/test/poly_recv_each.rb +35 -0
- data/vendor/spinel/test/poly_recv_each.rb.expected +7 -0
- data/vendor/spinel/test/poly_return_value_class.rb +66 -0
- data/vendor/spinel/test/poly_return_value_class.rb.expected +7 -0
- data/vendor/spinel/test/poly_self_deref.rb +28 -0
- data/vendor/spinel/test/poly_self_deref.rb.expected +2 -0
- data/vendor/spinel/test/poly_shl_array_push_dispatch.rb +35 -0
- data/vendor/spinel/test/poly_shl_array_push_dispatch.rb.expected +5 -0
- data/vendor/spinel/test/poly_slice_assign.rb +26 -0
- data/vendor/spinel/test/poly_slice_assign.rb.expected +3 -0
- data/vendor/spinel/test/poly_str_append.rb +40 -0
- data/vendor/spinel/test/poly_str_append.rb.expected +2 -0
- data/vendor/spinel/test/poly_to_i_via_str_poly_hash.rb +19 -0
- data/vendor/spinel/test/poly_to_i_via_str_poly_hash.rb.expected +1 -0
- data/vendor/spinel/test/post_execution.rb +17 -0
- data/vendor/spinel/test/post_execution.rb.expected +4 -0
- data/vendor/spinel/test/pre_execution.rb +18 -0
- data/vendor/spinel/test/pre_execution.rb.expected +4 -0
- data/vendor/spinel/test/primitive_method_shadow.rb +48 -0
- data/vendor/spinel/test/primitive_method_shadow.rb.expected +4 -0
- data/vendor/spinel/test/primitive_method_shadow_int_recv.rb +47 -0
- data/vendor/spinel/test/primitive_method_shadow_int_recv.rb.expected +1 -0
- data/vendor/spinel/test/proc.rb +42 -0
- data/vendor/spinel/test/proc.rb.expected +8 -0
- data/vendor/spinel/test/proc_closure.rb +44 -0
- data/vendor/spinel/test/proc_closure.rb.expected +8 -0
- data/vendor/spinel/test/proc_hash_value.rb +27 -0
- data/vendor/spinel/test/proc_hash_value.rb.expected +1 -0
- data/vendor/spinel/test/ptr_array.rb +107 -0
- data/vendor/spinel/test/ptr_array.rb.expected +23 -0
- data/vendor/spinel/test/range.rb +130 -0
- data/vendor/spinel/test/range.rb.expected +61 -0
- data/vendor/spinel/test/redo.rb +263 -0
- data/vendor/spinel/test/redo.rb.expected +71 -0
- data/vendor/spinel/test/reduce_acc_shadow.rb +31 -0
- data/vendor/spinel/test/reduce_acc_shadow.rb.expected +5 -0
- data/vendor/spinel/test/regex.rb +152 -0
- data/vendor/spinel/test/regex.rb.expected +43 -0
- data/vendor/spinel/test/regexp.rb +34 -0
- data/vendor/spinel/test/regexp.rb.expected +12 -0
- data/vendor/spinel/test/reopen_class.rb +33 -0
- data/vendor/spinel/test/reopen_class.rb.expected +5 -0
- data/vendor/spinel/test/require/lib/greeter.rb +9 -0
- data/vendor/spinel/test/require/main.rb +5 -0
- data/vendor/spinel/test/require_dedup/lib/data.rb +2 -0
- data/vendor/spinel/test/require_dedup/lib/types.rb +1 -0
- data/vendor/spinel/test/require_dedup/main.rb +8 -0
- data/vendor/spinel/test/rescue.rb +48 -0
- data/vendor/spinel/test/rescue.rb.expected +7 -0
- data/vendor/spinel/test/rescue_modifier_assign.rb +24 -0
- data/vendor/spinel/test/rescue_modifier_assign.rb.expected +4 -0
- data/vendor/spinel/test/rightward_assign.rb +36 -0
- data/vendor/spinel/test/rightward_assign.rb.expected +6 -0
- data/vendor/spinel/test/runtime_widen_int_to_poly_array.rb +27 -0
- data/vendor/spinel/test/runtime_widen_int_to_poly_array.rb.expected +3 -0
- data/vendor/spinel/test/sample_user_method.rb +20 -0
- data/vendor/spinel/test/scan_ivars_inherited_slot.rb +32 -0
- data/vendor/spinel/test/scan_new_calls_class_scope.rb +35 -0
- data/vendor/spinel/test/scan_new_calls_class_scope.rb.expected +1 -0
- data/vendor/spinel/test/send.rb +15 -0
- data/vendor/spinel/test/send.rb.expected +2 -0
- data/vendor/spinel/test/shareable_constant.rb +10 -0
- data/vendor/spinel/test/shareable_constant.rb.expected +2 -0
- data/vendor/spinel/test/sized_poly_array_nil_n.rb +37 -0
- data/vendor/spinel/test/sized_poly_array_nil_n.rb.expected +3 -0
- data/vendor/spinel/test/source_file.rb +37 -0
- data/vendor/spinel/test/source_file.rb.expected +8 -0
- data/vendor/spinel/test/splat_call.rb +72 -0
- data/vendor/spinel/test/splat_call.rb.expected +35 -0
- data/vendor/spinel/test/splat_destructure.rb +56 -0
- data/vendor/spinel/test/splat_destructure.rb.expected +36 -0
- data/vendor/spinel/test/splat_destructure_poly.rb +26 -0
- data/vendor/spinel/test/splat_destructure_poly.rb.expected +8 -0
- data/vendor/spinel/test/stdlib.rb +28 -0
- data/vendor/spinel/test/stdlib.rb.expected +5 -0
- data/vendor/spinel/test/stringio.rb +84 -0
- data/vendor/spinel/test/stringio.rb.expected +27 -0
- data/vendor/spinel/test/strip_nullable_int_cast.rb +24 -0
- data/vendor/spinel/test/strip_nullable_int_cast.rb.expected +1 -0
- data/vendor/spinel/test/struct.rb +25 -0
- data/vendor/spinel/test/struct.rb.expected +8 -0
- data/vendor/spinel/test/struct_inherit.rb +28 -0
- data/vendor/spinel/test/struct_inherit.rb.expected +7 -0
- data/vendor/spinel/test/struct_kw.rb +16 -0
- data/vendor/spinel/test/struct_kw.rb.expected +5 -0
- data/vendor/spinel/test/subclass_inherits_module_parent.rb +40 -0
- data/vendor/spinel/test/subclass_inherits_module_parent.rb.expected +3 -0
- data/vendor/spinel/test/super_arg_cross_class_cast.rb +39 -0
- data/vendor/spinel/test/super_arg_cross_class_cast.rb.expected +2 -0
- data/vendor/spinel/test/super_forwarding.rb +25 -0
- data/vendor/spinel/test/super_forwarding.rb.expected +3 -0
- data/vendor/spinel/test/sym_case.rb +15 -0
- data/vendor/spinel/test/sym_case.rb.expected +4 -0
- data/vendor/spinel/test/sym_poly_hash_merge.rb +47 -0
- data/vendor/spinel/test/sym_poly_hash_merge.rb.expected +11 -0
- data/vendor/spinel/test/symbol_ivar_reassign.rb +25 -0
- data/vendor/spinel/test/symbol_ivar_reassign.rb.expected +2 -0
- data/vendor/spinel/test/three_level_array_outer_index.rb +87 -0
- data/vendor/spinel/test/three_level_array_outer_index.rb.expected +17 -0
- data/vendor/spinel/test/toplevel_ivar_array.rb +19 -0
- data/vendor/spinel/test/toplevel_ivar_array.rb.expected +4 -0
- data/vendor/spinel/test/truncate_module_method.rb +46 -0
- data/vendor/spinel/test/truncate_module_method.rb.expected +6 -0
- data/vendor/spinel/test/unbox_poly_index_in_dispatch.rb +20 -0
- data/vendor/spinel/test/unbox_poly_index_in_dispatch.rb.expected +1 -0
- data/vendor/spinel/test/undef.rb +33 -0
- data/vendor/spinel/test/undef.rb.expected +2 -0
- data/vendor/spinel/test/user_method_named_like_mutator.rb +30 -0
- data/vendor/spinel/test/user_method_named_like_mutator.rb.expected +2 -0
- data/vendor/spinel/test/value_type_ctor_gc_save.rb +46 -0
- data/vendor/spinel/test/value_type_ctor_gc_save.rb.expected +3 -0
- data/vendor/spinel/test/widen_int_obj_to_poly.rb +29 -0
- data/vendor/spinel/test/yield.rb +67 -0
- data/vendor/spinel/test/yield.rb.expected +5 -0
- data/vendor/spinel/test/yield_method_call_in_method_body.rb +58 -0
- data/vendor/spinel/test/yield_method_call_in_method_body.rb.expected +4 -0
- metadata +778 -0
|
@@ -0,0 +1,1361 @@
|
|
|
1
|
+
/* Spinel Runtime Library */
|
|
2
|
+
#ifndef SP_RUNTIME_H
|
|
3
|
+
#define SP_RUNTIME_H
|
|
4
|
+
|
|
5
|
+
#ifdef __APPLE__
|
|
6
|
+
/* getcontext/makecontext/swapcontext are gated behind _XOPEN_SOURCE on
|
|
7
|
+
Darwin and marked deprecated since 10.6. _DARWIN_C_SOURCE re-enables
|
|
8
|
+
Darwin extensions (MAP_ANON, etc.) that _XOPEN_SOURCE alone would hide.
|
|
9
|
+
Suppress the deprecation warning so -Werror builds pass — we knowingly
|
|
10
|
+
use these APIs because Spinel's Fiber implementation depends on them. */
|
|
11
|
+
#define _XOPEN_SOURCE 600
|
|
12
|
+
#define _DARWIN_C_SOURCE
|
|
13
|
+
#pragma clang diagnostic push
|
|
14
|
+
#pragma clang diagnostic ignored "-Wdeprecated-declarations"
|
|
15
|
+
#endif
|
|
16
|
+
|
|
17
|
+
#include <stdio.h>
|
|
18
|
+
#include <stdlib.h>
|
|
19
|
+
#include <string.h>
|
|
20
|
+
#include <math.h>
|
|
21
|
+
#include <stdbool.h>
|
|
22
|
+
#include <stdint.h>
|
|
23
|
+
#include <ctype.h>
|
|
24
|
+
#include <stdarg.h>
|
|
25
|
+
#include <time.h>
|
|
26
|
+
#include <setjmp.h>
|
|
27
|
+
#ifdef _WIN32
|
|
28
|
+
#include <windows.h>
|
|
29
|
+
/* POSIX compat shims for MinGW */
|
|
30
|
+
#define mmap(a,l,p,f,fd,off) VirtualAlloc(NULL,(l),MEM_RESERVE|MEM_COMMIT,PAGE_READWRITE)
|
|
31
|
+
#define munmap(a,l) (VirtualFree((a),0,MEM_RELEASE)?0:-1)
|
|
32
|
+
#define MAP_FAILED NULL
|
|
33
|
+
#define PROT_READ 0
|
|
34
|
+
#define PROT_WRITE 0
|
|
35
|
+
#define MAP_PRIVATE 0
|
|
36
|
+
#define MAP_ANONYMOUS 0
|
|
37
|
+
#define MAP_NORESERVE 0
|
|
38
|
+
#else
|
|
39
|
+
#include <ucontext.h>
|
|
40
|
+
#include <unistd.h>
|
|
41
|
+
#include <sys/mman.h>
|
|
42
|
+
#endif
|
|
43
|
+
#if !defined(__APPLE__) && !defined(_WIN32)
|
|
44
|
+
#include <malloc.h>
|
|
45
|
+
#else
|
|
46
|
+
/* Darwin's libc has no malloc_trim; make it a no-op so call sites stay portable. */
|
|
47
|
+
#define malloc_trim(x) ((void)0)
|
|
48
|
+
#endif
|
|
49
|
+
#ifndef MAP_ANONYMOUS
|
|
50
|
+
#define MAP_ANONYMOUS MAP_ANON
|
|
51
|
+
#endif
|
|
52
|
+
|
|
53
|
+
typedef int64_t mrb_int;
|
|
54
|
+
typedef double mrb_float;
|
|
55
|
+
typedef bool mrb_bool;
|
|
56
|
+
/* sp_sym is defined per-program in emit_sym_runtime, but poly helpers
|
|
57
|
+
below need to reference it by forward declaration. */
|
|
58
|
+
typedef mrb_int sp_sym;
|
|
59
|
+
static const char *sp_sym_to_s(sp_sym id);
|
|
60
|
+
#ifndef TRUE
|
|
61
|
+
#define TRUE true
|
|
62
|
+
#endif
|
|
63
|
+
#ifndef FALSE
|
|
64
|
+
#define FALSE false
|
|
65
|
+
#endif
|
|
66
|
+
|
|
67
|
+
/* sp_raise_cls forward decl — defined later in this header (line ~1017).
|
|
68
|
+
Used by the integer-division helpers below to match CRuby semantics:
|
|
69
|
+
`a / 0`, `a % 0`, `a.divmod(0)`, `a.ceildiv(0)`, and `a.pow(e, 0)` all
|
|
70
|
+
raise ZeroDivisionError instead of triggering C undefined behaviour
|
|
71
|
+
(SIGFPE on x86) or silently returning 0. */
|
|
72
|
+
static void sp_raise_cls(const char *cls, const char *msg);
|
|
73
|
+
|
|
74
|
+
static inline mrb_int sp_idiv(mrb_int a, mrb_int b) {
|
|
75
|
+
if (b == 0) sp_raise_cls("ZeroDivisionError", "divided by 0");
|
|
76
|
+
mrb_int q = a / b; mrb_int r = a % b;
|
|
77
|
+
if ((r != 0) && ((r ^ b) < 0)) q--;
|
|
78
|
+
return q;
|
|
79
|
+
}
|
|
80
|
+
static inline mrb_int sp_imod(mrb_int a, mrb_int b) {
|
|
81
|
+
if (b == 0) sp_raise_cls("ZeroDivisionError", "divided by 0");
|
|
82
|
+
mrb_int r = a % b;
|
|
83
|
+
if ((r != 0) && ((r ^ b) < 0)) r += b;
|
|
84
|
+
return r;
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
static mrb_int sp_gcd(mrb_int a,mrb_int b){if(a<0)a=-a;if(b<0)b=-b;while(b){mrb_int t=b;b=a%b;a=t;}return a;}
|
|
88
|
+
static mrb_int sp_lcm(mrb_int a,mrb_int b){if(a==0||b==0)return 0;mrb_int g=sp_gcd(a,b);if(a<0)a=-a;if(b<0)b=-b;return (a/g)*b;}
|
|
89
|
+
static mrb_int sp_powmod(mrb_int base,mrb_int exp,mrb_int mod){if(mod==0)sp_raise_cls("ZeroDivisionError","divided by 0");mrb_int r=1;mrb_int m=mod<0?-mod:mod;if(m==1){r=0;}else{base=base%m;if(base<0)base+=m;while(exp>0){if(exp%2==1)r=r*base%m;exp=exp/2;base=base*base%m;}}if(mod<0&&r>0)r-=m;return r;}
|
|
90
|
+
static mrb_int sp_ceildiv(mrb_int a,mrb_int b){if(b==0)sp_raise_cls("ZeroDivisionError","divided by 0");if(b==-1)return -a;mrb_int q=a/b;if(a%b!=0&&((a^b)>=0))q++;return q;}
|
|
91
|
+
static mrb_int sp_int_clamp(mrb_int v,mrb_int lo,mrb_int hi){return v<lo?lo:v>hi?hi:v;}
|
|
92
|
+
/* Integer square root via Newton's method — exact for the full mrb_int
|
|
93
|
+
range (no double-precision rounding loss for n > 2^53). CRuby raises
|
|
94
|
+
on negative input; we mirror Spinel's other arithmetic helpers and
|
|
95
|
+
return 0 to avoid an exception path. */
|
|
96
|
+
static mrb_int sp_int_sqrt(mrb_int n){if(n<0)return 0;if(n<2)return n;mrb_int x=n,y=(x+1)/2;while(y<x){x=y;y=(x+n/x)/2;}return x;}
|
|
97
|
+
static inline char *sp_str_alloc_raw(size_t total_with_null); /* fwd decl */
|
|
98
|
+
static const char*sp_int_chr(mrb_int n){char*s=sp_str_alloc_raw(2);s[0]=(char)n;s[1]=0;return s;}
|
|
99
|
+
typedef struct{mrb_int first;mrb_int last;}sp_Range;
|
|
100
|
+
static sp_Range sp_range_new(mrb_int f,mrb_int l){sp_Range r;r.first=f;r.last=l;return r;}
|
|
101
|
+
|
|
102
|
+
/* ---- Time runtime ---- */
|
|
103
|
+
/* sp_Time keeps Time.now / Time.at as value-typed structs. d78149b's
|
|
104
|
+
sub-second precision is preserved by inlining tv_sec + tv_nsec/1e9
|
|
105
|
+
at every Time#to_f / Time#- call site (see spinel_codegen.rb). */
|
|
106
|
+
typedef struct { int64_t tv_sec; int32_t tv_nsec; } sp_Time;
|
|
107
|
+
static inline sp_Time sp_time_now(void) {
|
|
108
|
+
struct timespec ts;
|
|
109
|
+
clock_gettime(CLOCK_REALTIME, &ts);
|
|
110
|
+
return (sp_Time){ ts.tv_sec, (int32_t)ts.tv_nsec };
|
|
111
|
+
}
|
|
112
|
+
static inline sp_Time sp_time_at_int(mrb_int sec) {
|
|
113
|
+
return (sp_Time){ (int64_t)sec, 0 };
|
|
114
|
+
}
|
|
115
|
+
/* POSIX convention: keep tv_nsec in [0, 1e9). For negative epoch with a
|
|
116
|
+
non-integer fractional part, decrement tv_sec and roll the fraction
|
|
117
|
+
into the positive nsec range — so Time.at(-0.5).to_i returns -1, not 0. */
|
|
118
|
+
static inline sp_Time sp_time_at_float(mrb_float epoch) {
|
|
119
|
+
int64_t sec = (int64_t)epoch;
|
|
120
|
+
mrb_float frac = epoch - (mrb_float)sec;
|
|
121
|
+
if (frac < 0.0) {
|
|
122
|
+
sec -= 1;
|
|
123
|
+
frac += 1.0;
|
|
124
|
+
}
|
|
125
|
+
return (sp_Time){ sec, (int32_t)(frac * 1e9) };
|
|
126
|
+
}
|
|
127
|
+
|
|
128
|
+
typedef struct sp_gc_hdr { struct sp_gc_hdr *next; void (*finalize)(void *); void (*scan)(void *); size_t size; unsigned marked : 1; } sp_gc_hdr;
|
|
129
|
+
static sp_gc_hdr *sp_gc_heap = NULL; static size_t sp_gc_bytes = 0; static size_t sp_gc_threshold = 256*1024;
|
|
130
|
+
|
|
131
|
+
/* ---- String GC ---- */
|
|
132
|
+
typedef struct sp_str_hdr { struct sp_str_hdr *next; size_t size; } sp_str_hdr;
|
|
133
|
+
static sp_str_hdr *sp_str_heap = NULL;
|
|
134
|
+
#define SPL(s) (&("\xff" s)[1])
|
|
135
|
+
static const char sp_str_empty_data[] = "\xff";
|
|
136
|
+
#define sp_str_empty (sp_str_empty_data + 1)
|
|
137
|
+
|
|
138
|
+
static char *sp_str_alloc(size_t len) {
|
|
139
|
+
size_t total = sizeof(sp_str_hdr) + 1 + len + 1;
|
|
140
|
+
sp_str_hdr *h = (sp_str_hdr *)malloc(total);
|
|
141
|
+
h->next = sp_str_heap;
|
|
142
|
+
h->size = total;
|
|
143
|
+
sp_str_heap = h;
|
|
144
|
+
/* Don't fold string-heap pressure into sp_gc_bytes (issue #99): the
|
|
145
|
+
threshold heuristic in sp_gc_alloc is keyed on heap survivors, and
|
|
146
|
+
the str-heap mark-sweep that runs alongside (sp_str_sweep,
|
|
147
|
+
called from sp_gc_collect) doesn't add surviving strings back into
|
|
148
|
+
sp_gc_bytes. Each string alloc increments sp_gc_bytes by its full
|
|
149
|
+
size, but a sweep that reaps a string subtracts its size — and a
|
|
150
|
+
surviving string's size isn't re-added on the way out of
|
|
151
|
+
sp_gc_collect. Net effect on a workload that mixes heap allocs
|
|
152
|
+
with frequent small string allocs (e.g. `puts <float>` in a tight
|
|
153
|
+
loop): sp_gc_bytes drifts low after each collect, freed/before
|
|
154
|
+
looks artificially large, the threshold-recompute branch in
|
|
155
|
+
sp_gc_alloc takes the `sp_gc_bytes*4` path with a too-small base,
|
|
156
|
+
and the GC starts firing on every allocation. Strings are still
|
|
157
|
+
reaped via sp_str_sweep on every gc cycle, so dropping the
|
|
158
|
+
accounting only removes the threshold noise. */
|
|
159
|
+
char *body = (char *)(h + 1);
|
|
160
|
+
body[0] = (char)0xfe;
|
|
161
|
+
body[1 + len] = 0;
|
|
162
|
+
return body + 1;
|
|
163
|
+
}
|
|
164
|
+
|
|
165
|
+
static inline char *sp_str_alloc_raw(size_t total_with_null) {
|
|
166
|
+
return sp_str_alloc(total_with_null > 0 ? total_with_null - 1 : 0);
|
|
167
|
+
}
|
|
168
|
+
|
|
169
|
+
static const char *sp_str_dup_external(const char *s) {
|
|
170
|
+
if (!s) return NULL;
|
|
171
|
+
size_t n = strlen(s);
|
|
172
|
+
char *r = sp_str_alloc(n);
|
|
173
|
+
memcpy(r, s, n);
|
|
174
|
+
return r;
|
|
175
|
+
}
|
|
176
|
+
|
|
177
|
+
/* ---- UTF-8 helpers (used throughout the string runtime below) ---- */
|
|
178
|
+
static inline int sp_utf8_char_len(unsigned char c){if(c<0x80)return 1;if(c<0xC0)return 1;if(c<0xE0)return 2;if(c<0xF0)return 3;return 4;}
|
|
179
|
+
/* Bytes to advance past the codepoint at p (caller guarantees *p != 0).
|
|
180
|
+
Caps at NUL and validates the continuation-byte pattern, so malformed or
|
|
181
|
+
truncated UTF-8 never advances past the terminator. */
|
|
182
|
+
static inline int sp_utf8_advance(const char*p){int cn=sp_utf8_char_len((unsigned char)*p);int i=1;while(i<cn&&((unsigned char)p[i]&0xC0)==0x80)i++;return i;}
|
|
183
|
+
static inline int sp_utf8_decode(const char*p,uint32_t*out){unsigned char c=(unsigned char)p[0];if(c<0x80){*out=c;return 1;}if(c<0xC0){*out=c;return 1;}unsigned char c1=(unsigned char)p[1];if((c1&0xC0)!=0x80){*out=c;return 1;}if(c<0xE0){*out=((uint32_t)(c&0x1F)<<6)|(c1&0x3F);return 2;}unsigned char c2=(unsigned char)p[2];if((c2&0xC0)!=0x80){*out=c;return 1;}if(c<0xF0){*out=((uint32_t)(c&0x0F)<<12)|((uint32_t)(c1&0x3F)<<6)|(c2&0x3F);return 3;}unsigned char c3=(unsigned char)p[3];if((c3&0xC0)!=0x80){*out=c;return 1;}*out=((uint32_t)(c&0x07)<<18)|((uint32_t)(c1&0x3F)<<12)|((uint32_t)(c2&0x3F)<<6)|(c3&0x3F);return 4;}
|
|
184
|
+
static inline int sp_utf8_encode(uint32_t cp,char*out){if(cp<0x80){out[0]=(char)cp;return 1;}if(cp<0x800){out[0]=(char)(0xC0|(cp>>6));out[1]=(char)(0x80|(cp&0x3F));return 2;}if(cp<0x10000){out[0]=(char)(0xE0|(cp>>12));out[1]=(char)(0x80|((cp>>6)&0x3F));out[2]=(char)(0x80|(cp&0x3F));return 3;}out[0]=(char)(0xF0|(cp>>18));out[1]=(char)(0x80|((cp>>12)&0x3F));out[2]=(char)(0x80|((cp>>6)&0x3F));out[3]=(char)(0x80|(cp&0x3F));return 4;}
|
|
185
|
+
static mrb_int sp_str_length(const char*s){mrb_int n=0;while(*s){s+=sp_utf8_advance(s);n++;}return n;}
|
|
186
|
+
static mrb_int sp_str_ord(const char*s){if(!*s)return 0;uint32_t cp;sp_utf8_decode(s,&cp);return(mrb_int)cp;}
|
|
187
|
+
/* NULL-safe string equality. Issue #129: ENV[] returns NULL for unset vars
|
|
188
|
+
(the dispatch is `sp_str_dup_external(getenv(...))`, which propagates
|
|
189
|
+
NULL), so emitted strcmp(...) on the result of `ENV["X"] == "1"` would
|
|
190
|
+
dereference NULL on either side. nil-vs-string equality is false in
|
|
191
|
+
Ruby; nil == nil is true, so falling back to pointer equality on the
|
|
192
|
+
NULL path covers both. */
|
|
193
|
+
static inline int sp_str_eq(const char*a,const char*b){if(!a||!b)return a==b;return strcmp(a,b)==0;}
|
|
194
|
+
static size_t sp_utf8_byte_offset(const char*s,mrb_int char_idx){const char*p=s;while(char_idx>0&&*p){p+=sp_utf8_advance(p);char_idx--;}return(size_t)(p-s);}
|
|
195
|
+
static uint32_t*sp_utf8_decode_all(const char*s,size_t*out_n){size_t cap=8,n=0;uint32_t*cps=(uint32_t*)malloc(cap*sizeof(uint32_t));const char*p=s;while(*p){if(n>=cap){cap*=2;cps=(uint32_t*)realloc(cps,cap*sizeof(uint32_t));}uint32_t cp;p+=sp_utf8_decode(p,&cp);cps[n++]=cp;}*out_n=n;return cps;}
|
|
196
|
+
static int sp_utf8_set_has(const uint32_t*cps,size_t n,uint32_t cp){for(size_t i=0;i<n;i++)if(cps[i]==cp)return 1;return 0;}
|
|
197
|
+
|
|
198
|
+
static inline void sp_mark_string(const char *s) {
|
|
199
|
+
if (!s) return;
|
|
200
|
+
if ((unsigned char)s[-1] == 0xfe) {
|
|
201
|
+
((char *)s)[-1] = (char)0xfc;
|
|
202
|
+
}
|
|
203
|
+
}
|
|
204
|
+
|
|
205
|
+
static void sp_str_sweep(void) {
|
|
206
|
+
sp_str_hdr **pp = &sp_str_heap;
|
|
207
|
+
while (*pp) {
|
|
208
|
+
sp_str_hdr *h = *pp;
|
|
209
|
+
char *body = (char *)(h + 1);
|
|
210
|
+
if ((unsigned char)body[0] == 0xfc) {
|
|
211
|
+
body[0] = (char)0xfe;
|
|
212
|
+
pp = &h->next;
|
|
213
|
+
} else {
|
|
214
|
+
*pp = h->next;
|
|
215
|
+
/* String allocs no longer fold into sp_gc_bytes (see
|
|
216
|
+
sp_str_alloc); the matching subtract here drops too. */
|
|
217
|
+
free(h);
|
|
218
|
+
}
|
|
219
|
+
}
|
|
220
|
+
}
|
|
221
|
+
|
|
222
|
+
#define SP_GC_STACK_MAX 65536
|
|
223
|
+
static void **sp_gc_roots[SP_GC_STACK_MAX]; static int sp_gc_nroots = 0;
|
|
224
|
+
/* GC root tracking. SP_GC_ROOT registers a stack-resident root with a
|
|
225
|
+
cleanup-attribute sentinel so it's auto-popped when its declaring
|
|
226
|
+
scope ends — matches the variable's actual lifetime, including for
|
|
227
|
+
temporaries declared inside nested if/while/for blocks. The previous
|
|
228
|
+
form (push to global array, paired with SP_GC_SAVE/RESTORE at
|
|
229
|
+
function entry) leaked roots whose stack memory was reclaimed when
|
|
230
|
+
inner blocks returned, which clang's stricter stack layout exposed
|
|
231
|
+
as use-after-scope on inputs that nest scan_locals deeply (issue
|
|
232
|
+
surfaced on test/block2.rb under clang-built spinel_codegen). */
|
|
233
|
+
static inline int _sp_gc_root_push(void **p) {
|
|
234
|
+
if (sp_gc_nroots < SP_GC_STACK_MAX) { sp_gc_roots[sp_gc_nroots++] = p; return 1; }
|
|
235
|
+
return 0;
|
|
236
|
+
}
|
|
237
|
+
static inline void _sp_gc_root_pop(int *added) { if (*added) sp_gc_nroots--; }
|
|
238
|
+
#define _SP_GC_CONCAT2(a,b) a##b
|
|
239
|
+
#define _SP_GC_CONCAT(a,b) _SP_GC_CONCAT2(a,b)
|
|
240
|
+
#define SP_GC_SAVE() int __attribute__((cleanup(sp_gc_cleanup))) _gc_saved = sp_gc_nroots
|
|
241
|
+
#define SP_GC_ROOT(v) int __attribute__((cleanup(_sp_gc_root_pop))) _SP_GC_CONCAT(_sp_gcr_, __COUNTER__) = _sp_gc_root_push((void**)&(v))
|
|
242
|
+
#define SP_GC_RESTORE() sp_gc_nroots = _gc_saved
|
|
243
|
+
#define SP_GC_MARK_STACK_MAX (1024*64)
|
|
244
|
+
static void**sp_gc_mark_stack=NULL;static int sp_gc_mark_top=0;
|
|
245
|
+
static void sp_gc_mark(void*obj){if(!obj)return;unsigned char pm=((unsigned char*)obj)[-1];if(pm==0xfe){((char*)obj)[-1]=(char)0xfc;return;}if(pm==0xfc||pm==0xff)return;sp_gc_hdr*h=(sp_gc_hdr*)((char*)obj-sizeof(sp_gc_hdr));if(h->marked)return;h->marked=1;if(h->scan){if(sp_gc_mark_stack&&sp_gc_mark_top<SP_GC_MARK_STACK_MAX){sp_gc_mark_stack[sp_gc_mark_top++]=obj;}else{h->scan(obj);}}}
|
|
246
|
+
/* Forward decl: defined alongside the regex globals it marks. */
|
|
247
|
+
static void sp_re_mark_globals(void);
|
|
248
|
+
static void sp_gc_mark_all(void){if(!sp_gc_mark_stack)sp_gc_mark_stack=(void**)malloc(sizeof(void*)*SP_GC_MARK_STACK_MAX);sp_gc_mark_top=0;for(int i=0;i<sp_gc_nroots;i++){void*obj=*sp_gc_roots[i];if(obj)sp_gc_mark(obj);}sp_re_mark_globals();while(sp_gc_mark_top>0){void*obj=sp_gc_mark_stack[--sp_gc_mark_top];sp_gc_hdr*h=(sp_gc_hdr*)((char*)obj-sizeof(sp_gc_hdr));if(h->scan)h->scan(obj);}}
|
|
249
|
+
static void sp_gc_cleanup(int*p){sp_gc_nroots=*p;}
|
|
250
|
+
#define SP_GC_NBUCKETS 32
|
|
251
|
+
static sp_gc_hdr*sp_gc_buckets[SP_GC_NBUCKETS];
|
|
252
|
+
static inline int sp_gc_bucket(size_t sz){int b=(int)(sz/16);return b<SP_GC_NBUCKETS?b:SP_GC_NBUCKETS-1;}
|
|
253
|
+
static int sp_gc_cycle=0;
|
|
254
|
+
static sp_gc_hdr*sp_gc_old_heap=NULL;static size_t sp_gc_old_bytes=0;
|
|
255
|
+
#define SP_GC_FULL_INTERVAL 8
|
|
256
|
+
static void sp_gc_collect(void){int full=(sp_gc_cycle%SP_GC_FULL_INTERVAL==0);sp_gc_cycle++;sp_gc_hdr*hh=sp_gc_old_heap;while(hh){hh->marked=0;hh=hh->next;}sp_gc_mark_all();if(full){sp_gc_hdr**pp=&sp_gc_old_heap;sp_gc_old_bytes=0;while(*pp){sp_gc_hdr*h=*pp;if(!h->marked){*pp=h->next;if(h->finalize)h->finalize((char*)h+sizeof(sp_gc_hdr));free(h);}else{h->marked=1;sp_gc_old_bytes+=h->size;pp=&h->next;}}}else{hh=sp_gc_old_heap;while(hh){hh->marked=1;hh=hh->next;}}sp_gc_hdr**pp=&sp_gc_heap;sp_gc_bytes=sp_gc_old_bytes;while(*pp){sp_gc_hdr*h=*pp;if(!h->marked){*pp=h->next;if(h->finalize)h->finalize((char*)h+sizeof(sp_gc_hdr));free(h);}else{h->marked=1;*pp=h->next;h->next=sp_gc_old_heap;sp_gc_old_heap=h;sp_gc_old_bytes+=h->size;sp_gc_bytes+=h->size;}}sp_str_sweep();if(full)malloc_trim(0);}
|
|
257
|
+
static size_t sp_gc_threshold_init=256*1024;
|
|
258
|
+
void*sp_gc_alloc(size_t sz,void(*fin)(void*),void(*scn)(void*)){if(sp_gc_bytes>sp_gc_threshold){size_t before=sp_gc_bytes;sp_gc_collect();size_t freed=before-sp_gc_bytes;if(freed<before/4){sp_gc_threshold=before*2;}else if(sp_gc_bytes>0){sp_gc_threshold=sp_gc_bytes*4;if(sp_gc_threshold<sp_gc_threshold_init)sp_gc_threshold=sp_gc_threshold_init;}else{sp_gc_threshold=sp_gc_threshold_init;}}size_t need=sizeof(sp_gc_hdr)+sz;sp_gc_hdr*h=(sp_gc_hdr*)calloc(1,need);h->finalize=fin;h->scan=scn;h->size=need;h->marked=0;h->next=sp_gc_heap;sp_gc_heap=h;sp_gc_bytes+=need;return(char*)h+sizeof(sp_gc_hdr);}
|
|
259
|
+
void*sp_gc_alloc_nogc(size_t sz,void(*fin)(void*),void(*scn)(void*)){size_t need=sizeof(sp_gc_hdr)+sz;sp_gc_hdr*h=(sp_gc_hdr*)calloc(1,need);h->finalize=fin;h->scan=scn;h->size=need;h->marked=0;h->next=sp_gc_heap;sp_gc_heap=h;sp_gc_bytes+=need;return(char*)h+sizeof(sp_gc_hdr);}
|
|
260
|
+
|
|
261
|
+
/* `Object.new` — a sentinel object whose only meaningful property is
|
|
262
|
+
identity. Each call returns a fresh GC-managed allocation, so two
|
|
263
|
+
`Object.new` results compare as `!=` via their pointer addresses. */
|
|
264
|
+
typedef struct sp_Object_s { uint8_t _pad; } sp_Object;
|
|
265
|
+
static sp_Object *sp_Object_new(void){return(sp_Object*)sp_gc_alloc(sizeof(sp_Object),NULL,NULL);}
|
|
266
|
+
|
|
267
|
+
typedef struct{mrb_int*data;mrb_int start;mrb_int len;mrb_int cap;}sp_IntArray;
|
|
268
|
+
static void sp_IntArray_fin(void*p){free(((sp_IntArray*)p)->data);}
|
|
269
|
+
static sp_IntArray*sp_IntArray_new(void){sp_IntArray*a=(sp_IntArray*)sp_gc_alloc(sizeof(sp_IntArray),sp_IntArray_fin,NULL);a->cap=16;a->data=(mrb_int*)malloc(sizeof(mrb_int)*a->cap);a->start=0;a->len=0;{sp_gc_hdr*h=(sp_gc_hdr*)((char*)a-sizeof(sp_gc_hdr));h->size+=sizeof(mrb_int)*a->cap;sp_gc_bytes+=sizeof(mrb_int)*a->cap;}return a;}
|
|
270
|
+
static sp_IntArray*sp_IntArray_from_range(mrb_int s,mrb_int e){sp_IntArray*a=sp_IntArray_new();mrb_int n=e-s+1;if(n<0)n=0;if(n>a->cap){sp_gc_hdr*h=(sp_gc_hdr*)((char*)a-sizeof(sp_gc_hdr));sp_gc_bytes-=sizeof(mrb_int)*a->cap;h->size-=sizeof(mrb_int)*a->cap;a->cap=n;a->data=(mrb_int*)realloc(a->data,sizeof(mrb_int)*a->cap);h->size+=sizeof(mrb_int)*a->cap;sp_gc_bytes+=sizeof(mrb_int)*a->cap;}for(mrb_int i=0;i<n;i++)a->data[i]=s+i;a->len=n;return a;}
|
|
271
|
+
static sp_IntArray*sp_IntArray_dup(sp_IntArray*a){sp_IntArray*b=sp_IntArray_new();if(a->len>b->cap){sp_gc_hdr*h=(sp_gc_hdr*)((char*)b-sizeof(sp_gc_hdr));sp_gc_bytes-=sizeof(mrb_int)*b->cap;h->size-=sizeof(mrb_int)*b->cap;b->cap=a->len;b->data=(mrb_int*)realloc(b->data,sizeof(mrb_int)*b->cap);h->size+=sizeof(mrb_int)*b->cap;sp_gc_bytes+=sizeof(mrb_int)*b->cap;}memcpy(b->data,a->data+a->start,sizeof(mrb_int)*a->len);b->len=a->len;return b;}
|
|
272
|
+
/* a[start, len] / a[start..end] for IntArray. Negative start counts from
|
|
273
|
+
* the end. start past the array length yields an empty result; len is
|
|
274
|
+
* clamped so we never read past the source. CRuby returns nil for
|
|
275
|
+
* out-of-bounds start; we return an empty IntArray since this typed
|
|
276
|
+
* collection has no nullable form. */
|
|
277
|
+
static sp_IntArray*sp_IntArray_slice(sp_IntArray*a,mrb_int start,mrb_int len){if(start<0)start+=a->len;if(start<0)start=0;sp_IntArray*b=sp_IntArray_new();if(start>=a->len||len<=0)return b;if(start+len>a->len)len=a->len-start;if(len>b->cap){sp_gc_hdr*h=(sp_gc_hdr*)((char*)b-sizeof(sp_gc_hdr));sp_gc_bytes-=sizeof(mrb_int)*b->cap;h->size-=sizeof(mrb_int)*b->cap;b->cap=len;b->data=(mrb_int*)realloc(b->data,sizeof(mrb_int)*b->cap);h->size+=sizeof(mrb_int)*b->cap;sp_gc_bytes+=sizeof(mrb_int)*b->cap;}memcpy(b->data,a->data+a->start+start,sizeof(mrb_int)*len);b->len=len;return b;}
|
|
278
|
+
static void sp_IntArray_replace(sp_IntArray*dst,sp_IntArray*src){dst->len=0;dst->start=0;if(src->len>dst->cap){sp_gc_hdr*h=(sp_gc_hdr*)((char*)dst-sizeof(sp_gc_hdr));sp_gc_bytes-=sizeof(mrb_int)*dst->cap;h->size-=sizeof(mrb_int)*dst->cap;void*nd=realloc(dst->data,sizeof(mrb_int)*src->len);if(!nd){perror("realloc");exit(1);}dst->data=(mrb_int*)nd;dst->cap=src->len;h->size+=sizeof(mrb_int)*dst->cap;sp_gc_bytes+=sizeof(mrb_int)*dst->cap;}memcpy(dst->data,src->data+src->start,sizeof(mrb_int)*src->len);dst->len=src->len;}
|
|
279
|
+
static void __attribute__((noinline)) sp_IntArray_push_grow(sp_IntArray*a){if(a->start>0){memmove(a->data,a->data+a->start,sizeof(mrb_int)*a->len);a->start=0;if(a->len<a->cap)return;}{sp_gc_hdr*h=(sp_gc_hdr*)((char*)a-sizeof(sp_gc_hdr));sp_gc_bytes-=sizeof(mrb_int)*a->cap;h->size-=sizeof(mrb_int)*a->cap;a->cap=a->cap*2+1;a->data=(mrb_int*)realloc(a->data,sizeof(mrb_int)*a->cap);h->size+=sizeof(mrb_int)*a->cap;sp_gc_bytes+=sizeof(mrb_int)*a->cap;}}
|
|
280
|
+
static inline void sp_IntArray_push(sp_IntArray*a,mrb_int v){if(a->start+a->len>=a->cap)sp_IntArray_push_grow(a);a->data[a->start+a->len]=v;a->len++;}
|
|
281
|
+
static inline mrb_int sp_IntArray_pop(sp_IntArray*a){return a->data[a->start+--a->len];}
|
|
282
|
+
static inline mrb_int sp_IntArray_shift(sp_IntArray*a){mrb_int v=a->data[a->start];a->start++;a->len--;return v;}
|
|
283
|
+
static inline mrb_int sp_IntArray_length(sp_IntArray*a){return a->len;}
|
|
284
|
+
static inline mrb_bool sp_IntArray_empty(sp_IntArray*a){return a->len==0;}
|
|
285
|
+
static inline mrb_int sp_IntArray_get(sp_IntArray*a,mrb_int i){if(i<0)i+=a->len;return a->data[a->start+i];}
|
|
286
|
+
static void sp_IntArray_set_slow(sp_IntArray*a,mrb_int i,mrb_int v){while(a->start+i>=a->cap){sp_gc_hdr*h=(sp_gc_hdr*)((char*)a-sizeof(sp_gc_hdr));sp_gc_bytes-=sizeof(mrb_int)*a->cap;h->size-=sizeof(mrb_int)*a->cap;a->cap=a->cap*2+1;a->data=(mrb_int*)realloc(a->data,sizeof(mrb_int)*a->cap);h->size+=sizeof(mrb_int)*a->cap;sp_gc_bytes+=sizeof(mrb_int)*a->cap;}while(i>=a->len){a->data[a->start+a->len]=0;a->len++;}a->data[a->start+i]=v;}
|
|
287
|
+
static inline void sp_IntArray_set(sp_IntArray*a,mrb_int i,mrb_int v){if(i<0)i+=a->len;if(i>=0&&i<a->len){a->data[a->start+i]=v;return;}sp_IntArray_set_slow(a,i,v);}
|
|
288
|
+
static void sp_IntArray_reverse_bang(sp_IntArray*a){for(mrb_int i=0,j=a->len-1;i<j;i++,j--){mrb_int t=a->data[a->start+i];a->data[a->start+i]=a->data[a->start+j];a->data[a->start+j]=t;}}
|
|
289
|
+
static void sp_IntArray_rotate_bang(sp_IntArray*a,mrb_int n){if(a->len<=0)return;n=((n%a->len)+a->len)%a->len;if(n==0)return;mrb_int*tmp=(mrb_int*)malloc(sizeof(mrb_int)*a->len);for(mrb_int i=0;i<a->len;i++)tmp[i]=a->data[a->start+(i+n)%a->len];for(mrb_int i=0;i<a->len;i++)a->data[a->start+i]=tmp[i];free(tmp);}
|
|
290
|
+
static int _sp_int_cmp(const void*a,const void*b){mrb_int va=*(const mrb_int*)a,vb=*(const mrb_int*)b;return(va>vb)-(va<vb);}
|
|
291
|
+
static sp_IntArray*sp_IntArray_sort(sp_IntArray*a){sp_IntArray*b=sp_IntArray_dup(a);qsort(b->data+b->start,b->len,sizeof(mrb_int),_sp_int_cmp);return b;}
|
|
292
|
+
static void sp_IntArray_sort_bang(sp_IntArray*a){qsort(a->data+a->start,a->len,sizeof(mrb_int),_sp_int_cmp);}
|
|
293
|
+
static void sp_IntArray_shuffle_bang(sp_IntArray*a){for(mrb_int i=a->len-1;i>0;i--){mrb_int j=(mrb_int)(rand()%(i+1));mrb_int t=a->data[a->start+i];a->data[a->start+i]=a->data[a->start+j];a->data[a->start+j]=t;}}
|
|
294
|
+
static sp_IntArray*sp_IntArray_shuffle(sp_IntArray*a){sp_IntArray*b=sp_IntArray_dup(a);sp_IntArray_shuffle_bang(b);return b;}
|
|
295
|
+
static mrb_int sp_IntArray_min(sp_IntArray*a){mrb_int m=a->data[a->start];for(mrb_int i=1;i<a->len;i++)if(a->data[a->start+i]<m)m=a->data[a->start+i];return m;}
|
|
296
|
+
static mrb_int sp_IntArray_max(sp_IntArray*a){mrb_int m=a->data[a->start];for(mrb_int i=1;i<a->len;i++)if(a->data[a->start+i]>m)m=a->data[a->start+i];return m;}
|
|
297
|
+
static mrb_int sp_IntArray_sum(sp_IntArray*a){mrb_int s=0;for(mrb_int i=0;i<a->len;i++)s+=a->data[a->start+i];return s;}
|
|
298
|
+
static mrb_bool sp_IntArray_include(sp_IntArray*a,mrb_int v){for(mrb_int i=0;i<a->len;i++)if(a->data[a->start+i]==v)return TRUE;return FALSE;}
|
|
299
|
+
static mrb_int sp_IntArray_index(sp_IntArray*a,mrb_int v){for(mrb_int i=0;i<a->len;i++)if(a->data[a->start+i]==v)return i;return -1;}
|
|
300
|
+
static mrb_int sp_IntArray_rindex(sp_IntArray*a,mrb_int v){for(mrb_int i=a->len-1;i>=0;i--)if(a->data[a->start+i]==v)return i;return -1;}
|
|
301
|
+
static mrb_int sp_IntArray_delete_at(sp_IntArray*a,mrb_int i){if(i<0)i+=a->len;if(i<0||i>=a->len)return 0;mrb_int v=a->data[a->start+i];for(mrb_int j=i;j<a->len-1;j++)a->data[a->start+j]=a->data[a->start+j+1];a->len--;return v;}
|
|
302
|
+
static mrb_int sp_IntArray_delete(sp_IntArray*a,mrb_int v){mrb_int w=0;for(mrb_int i=0;i<a->len;i++){if(a->data[a->start+i]!=v){a->data[a->start+w]=a->data[a->start+i];w++;}}mrb_int d=a->len-w;a->len=w;return d>0?v:0;}
|
|
303
|
+
static void sp_IntArray_insert(sp_IntArray*a,mrb_int i,mrb_int v){if(i<0)i+=a->len+1;sp_IntArray_push(a,0);for(mrb_int j=a->len-1;j>i;j--)a->data[a->start+j]=a->data[a->start+j-1];a->data[a->start+i]=v;}
|
|
304
|
+
static sp_IntArray*sp_int_digits(mrb_int n,mrb_int base){sp_IntArray*a=sp_IntArray_new();if(base<2)base=10;if(n==0){sp_IntArray_push(a,0);return a;}if(n<0)n=-n;while(n>0){sp_IntArray_push(a,n%base);n/=base;}return a;}
|
|
305
|
+
static sp_IntArray*sp_IntArray_uniq(sp_IntArray*a){sp_IntArray*b=sp_IntArray_new();for(mrb_int i=0;i<a->len;i++){int found=0;for(mrb_int j=0;j<b->len;j++){if(b->data[b->start+j]==a->data[a->start+i]){found=1;break;}}if(!found)sp_IntArray_push(b,a->data[a->start+i]);}return b;}
|
|
306
|
+
static sp_IntArray*sp_IntArray_intersect(sp_IntArray*a,sp_IntArray*b){sp_IntArray*r=sp_IntArray_new();for(mrb_int i=0;i<a->len;i++){mrb_int v=a->data[a->start+i];if(sp_IntArray_include(b,v)&&!sp_IntArray_include(r,v))sp_IntArray_push(r,v);}return r;}
|
|
307
|
+
static sp_IntArray*sp_IntArray_union(sp_IntArray*a,sp_IntArray*b){sp_IntArray*r=sp_IntArray_new();for(mrb_int i=0;i<a->len;i++){mrb_int v=a->data[a->start+i];if(!sp_IntArray_include(r,v))sp_IntArray_push(r,v);}for(mrb_int i=0;i<b->len;i++){mrb_int v=b->data[b->start+i];if(!sp_IntArray_include(r,v))sp_IntArray_push(r,v);}return r;}
|
|
308
|
+
/* Array#- / Array#difference: keep every LHS element NOT in RHS,
|
|
309
|
+
preserving the LHS's duplicates. CRuby's Array#- is not a set
|
|
310
|
+
subtraction — `[1,1,2,3] - [3]` is `[1,1,2]`, not `[1,2]`. */
|
|
311
|
+
static sp_IntArray*sp_IntArray_difference(sp_IntArray*a,sp_IntArray*b){sp_IntArray*r=sp_IntArray_new();for(mrb_int i=0;i<a->len;i++){mrb_int v=a->data[a->start+i];if(!sp_IntArray_include(b,v))sp_IntArray_push(r,v);}return r;}
|
|
312
|
+
static void sp_IntArray_unshift(sp_IntArray*a,mrb_int v){if(a->start>0){a->start--;a->data[a->start]=v;a->len++;}else{mrb_int e=a->len+1;if(e>a->cap){sp_gc_hdr*h=(sp_gc_hdr*)((char*)a-sizeof(sp_gc_hdr));sp_gc_bytes-=sizeof(mrb_int)*a->cap;h->size-=sizeof(mrb_int)*a->cap;a->cap=a->cap*2+1;a->data=(mrb_int*)realloc(a->data,sizeof(mrb_int)*a->cap);h->size+=sizeof(mrb_int)*a->cap;sp_gc_bytes+=sizeof(mrb_int)*a->cap;}memmove(a->data+1,a->data,sizeof(mrb_int)*a->len);a->data[0]=v;a->len++;}}
|
|
313
|
+
static const char*sp_IntArray_join(sp_IntArray*a,const char*sep){size_t sl=strlen(sep),cap=256;char*buf=(char*)malloc(cap);size_t len=0;for(mrb_int i=0;i<a->len;i++){if(i>0){if(len+sl>=cap){cap*=2;buf=(char*)realloc(buf,cap);}memcpy(buf+len,sep,sl);len+=sl;}char tmp[32];int n=snprintf(tmp,32,"%lld",(long long)a->data[a->start+i]);if(len+n>=cap){cap*=2;buf=(char*)realloc(buf,cap);}memcpy(buf+len,tmp,n);len+=n;}buf[len]=0;char*r=sp_str_alloc(len);memcpy(r,buf,len);free(buf);return r;}
|
|
314
|
+
static mrb_bool sp_IntArray_eq(sp_IntArray*a,sp_IntArray*b){if(a->len!=b->len)return FALSE;for(mrb_int i=0;i<a->len;i++)if(a->data[a->start+i]!=b->data[b->start+i])return FALSE;return TRUE;}
|
|
315
|
+
|
|
316
|
+
typedef struct{mrb_float*data;mrb_int len;mrb_int cap;}sp_FloatArray;
|
|
317
|
+
static void sp_FloatArray_fin(void*p){sp_FloatArray*a=(sp_FloatArray*)p;sp_gc_hdr*h=(sp_gc_hdr*)((char*)a-sizeof(sp_gc_hdr));sp_gc_bytes-=sizeof(mrb_float)*a->cap;h->size-=sizeof(mrb_float)*a->cap;free(a->data);}
|
|
318
|
+
static sp_FloatArray*sp_FloatArray_new(void){sp_FloatArray*a=(sp_FloatArray*)sp_gc_alloc(sizeof(sp_FloatArray),sp_FloatArray_fin,NULL);a->cap=16;a->data=(mrb_float*)malloc(sizeof(mrb_float)*a->cap);a->len=0;{sp_gc_hdr*h=(sp_gc_hdr*)((char*)a-sizeof(sp_gc_hdr));h->size+=sizeof(mrb_float)*a->cap;sp_gc_bytes+=sizeof(mrb_float)*a->cap;}return a;}
|
|
319
|
+
static inline void sp_FloatArray_push(sp_FloatArray*a,mrb_float v){if(a->len>=a->cap){sp_gc_hdr*h=(sp_gc_hdr*)((char*)a-sizeof(sp_gc_hdr));sp_gc_bytes-=sizeof(mrb_float)*a->cap;h->size-=sizeof(mrb_float)*a->cap;a->cap=a->cap*2+1;a->data=(mrb_float*)realloc(a->data,sizeof(mrb_float)*a->cap);h->size+=sizeof(mrb_float)*a->cap;sp_gc_bytes+=sizeof(mrb_float)*a->cap;}a->data[a->len++]=v;}
|
|
320
|
+
static mrb_float sp_FloatArray_min(sp_FloatArray*a){if(a->len==0)return 0;mrb_float m=a->data[0];for(mrb_int i=1;i<a->len;i++)if(a->data[i]<m)m=a->data[i];return m;}
|
|
321
|
+
static mrb_float sp_FloatArray_max(sp_FloatArray*a){if(a->len==0)return 0;mrb_float m=a->data[0];for(mrb_int i=1;i<a->len;i++)if(a->data[i]>m)m=a->data[i];return m;}
|
|
322
|
+
static mrb_float sp_FloatArray_sum(sp_FloatArray*a){mrb_float s=0;for(mrb_int i=0;i<a->len;i++)s+=a->data[i];return s;}
|
|
323
|
+
static void sp_FloatArray_replace(sp_FloatArray*dst,sp_FloatArray*src){dst->len=0;if(src->len>dst->cap){sp_gc_hdr*h=(sp_gc_hdr*)((char*)dst-sizeof(sp_gc_hdr));sp_gc_bytes-=sizeof(mrb_float)*dst->cap;h->size-=sizeof(mrb_float)*dst->cap;void*nd=realloc(dst->data,sizeof(mrb_float)*src->len);if(!nd){perror("realloc");exit(1);}dst->data=(mrb_float*)nd;dst->cap=src->len;h->size+=sizeof(mrb_float)*dst->cap;sp_gc_bytes+=sizeof(mrb_float)*dst->cap;}memcpy(dst->data,src->data,sizeof(mrb_float)*src->len);dst->len=src->len;}
|
|
324
|
+
static inline mrb_float sp_FloatArray_pop(sp_FloatArray*a){return a->data[--a->len];}
|
|
325
|
+
static inline mrb_float sp_FloatArray_shift(sp_FloatArray*a){if(a->len==0)return 0.0;mrb_float v=a->data[0];for(mrb_int i=0;i+1<a->len;i++)a->data[i]=a->data[i+1];a->len--;return v;}
|
|
326
|
+
static inline mrb_int sp_FloatArray_length(sp_FloatArray*a){return a->len;}
|
|
327
|
+
static inline mrb_bool sp_FloatArray_empty(sp_FloatArray*a){return a->len==0;}
|
|
328
|
+
static inline mrb_float sp_FloatArray_get(sp_FloatArray*a,mrb_int i){if(i<0)i+=a->len;return a->data[i];}
|
|
329
|
+
/* a[start, len] / a[start..end] for FloatArray. Same negative-start
|
|
330
|
+
* and length-clamping semantics as sp_IntArray_slice. */
|
|
331
|
+
static sp_FloatArray*sp_FloatArray_slice(sp_FloatArray*a,mrb_int start,mrb_int len){if(start<0)start+=a->len;if(start<0)start=0;sp_FloatArray*b=sp_FloatArray_new();if(start>=a->len||len<=0)return b;if(start+len>a->len)len=a->len-start;if(len>b->cap){sp_gc_hdr*h=(sp_gc_hdr*)((char*)b-sizeof(sp_gc_hdr));sp_gc_bytes-=sizeof(mrb_float)*b->cap;h->size-=sizeof(mrb_float)*b->cap;b->cap=len;b->data=(mrb_float*)realloc(b->data,sizeof(mrb_float)*b->cap);h->size+=sizeof(mrb_float)*b->cap;sp_gc_bytes+=sizeof(mrb_float)*b->cap;}memcpy(b->data,a->data+start,sizeof(mrb_float)*len);b->len=len;return b;}
|
|
332
|
+
static inline void sp_FloatArray_set(sp_FloatArray*a,mrb_int i,mrb_float v){if(i<0)i+=a->len;while(i>=a->cap){a->cap=a->cap*2+1;a->data=(mrb_float*)realloc(a->data,sizeof(mrb_float)*a->cap);}while(i>=a->len){a->data[a->len]=0.0;a->len++;}a->data[i]=v;}
|
|
333
|
+
static void sp_FloatArray_reverse_bang(sp_FloatArray*a){for(mrb_int i=0,j=a->len-1;i<j;i++,j--){mrb_float t=a->data[i];a->data[i]=a->data[j];a->data[j]=t;}}
|
|
334
|
+
static void sp_FloatArray_rotate_bang(sp_FloatArray*a,mrb_int n){if(a->len<=0)return;n=((n%a->len)+a->len)%a->len;if(n==0)return;mrb_float*tmp=(mrb_float*)malloc(sizeof(mrb_float)*a->len);for(mrb_int i=0;i<a->len;i++)tmp[i]=a->data[(i+n)%a->len];for(mrb_int i=0;i<a->len;i++)a->data[i]=tmp[i];free(tmp);}
|
|
335
|
+
static int _sp_float_cmp(const void*a,const void*b){mrb_float va=*(const mrb_float*)a,vb=*(const mrb_float*)b;return(va>vb)-(va<vb);}
|
|
336
|
+
static void sp_FloatArray_sort_bang(sp_FloatArray*a){qsort(a->data,a->len,sizeof(mrb_float),_sp_float_cmp);}
|
|
337
|
+
static void sp_FloatArray_shuffle_bang(sp_FloatArray*a){for(mrb_int i=a->len-1;i>0;i--){mrb_int j=(mrb_int)(rand()%(i+1));mrb_float t=a->data[i];a->data[i]=a->data[j];a->data[j]=t;}}
|
|
338
|
+
static sp_FloatArray*sp_FloatArray_shuffle(sp_FloatArray*a){sp_FloatArray*r=sp_FloatArray_new();sp_FloatArray_replace(r,a);sp_FloatArray_shuffle_bang(r);return r;}
|
|
339
|
+
/* IEEE 754 == on mrb_float: NaN never matches; +0.0 == -0.0 (diverges from Float#eql?). */
|
|
340
|
+
static mrb_bool sp_FloatArray_include(sp_FloatArray*a,mrb_float v){for(mrb_int i=0;i<a->len;i++)if(a->data[i]==v)return TRUE;return FALSE;}
|
|
341
|
+
static sp_FloatArray*sp_FloatArray_intersect(sp_FloatArray*a,sp_FloatArray*b){sp_FloatArray*r=sp_FloatArray_new();for(mrb_int i=0;i<a->len;i++){mrb_float v=a->data[i];if(sp_FloatArray_include(b,v)&&!sp_FloatArray_include(r,v))sp_FloatArray_push(r,v);}return r;}
|
|
342
|
+
static sp_FloatArray*sp_FloatArray_union(sp_FloatArray*a,sp_FloatArray*b){sp_FloatArray*r=sp_FloatArray_new();for(mrb_int i=0;i<a->len;i++){mrb_float v=a->data[i];if(!sp_FloatArray_include(r,v))sp_FloatArray_push(r,v);}for(mrb_int i=0;i<b->len;i++){mrb_float v=b->data[i];if(!sp_FloatArray_include(r,v))sp_FloatArray_push(r,v);}return r;}
|
|
343
|
+
static sp_FloatArray*sp_FloatArray_difference(sp_FloatArray*a,sp_FloatArray*b){sp_FloatArray*r=sp_FloatArray_new();for(mrb_int i=0;i<a->len;i++){mrb_float v=a->data[i];if(!sp_FloatArray_include(b,v))sp_FloatArray_push(r,v);}return r;}
|
|
344
|
+
|
|
345
|
+
/* ---- PtrArray: array of void* pointers ---- */
|
|
346
|
+
typedef struct{void**data;mrb_int len;mrb_int cap;void(*scan_elem)(void*);}sp_PtrArray;
|
|
347
|
+
static void sp_PtrArray_fin(void*p){sp_PtrArray*a=(sp_PtrArray*)p;sp_gc_hdr*h=(sp_gc_hdr*)((char*)a-sizeof(sp_gc_hdr));sp_gc_bytes-=sizeof(void*)*a->cap;h->size-=sizeof(void*)*a->cap;free(a->data);}
|
|
348
|
+
static void sp_PtrArray_gc_scan(void*p){sp_PtrArray*a=(sp_PtrArray*)p;if(!a->scan_elem)return;for(mrb_int i=0;i<a->len;i++){if(a->data[i])a->scan_elem(a->data[i]);}}
|
|
349
|
+
static sp_PtrArray*sp_PtrArray_new_scan(void(*scan_elem)(void*)){sp_PtrArray*a=(sp_PtrArray*)sp_gc_alloc(sizeof(sp_PtrArray),sp_PtrArray_fin,scan_elem?sp_PtrArray_gc_scan:NULL);a->cap=16;a->data=(void**)malloc(sizeof(void*)*a->cap);a->len=0;a->scan_elem=scan_elem;{sp_gc_hdr*h=(sp_gc_hdr*)((char*)a-sizeof(sp_gc_hdr));h->size+=sizeof(void*)*a->cap;sp_gc_bytes+=sizeof(void*)*a->cap;}return a;}
|
|
350
|
+
static sp_PtrArray*sp_PtrArray_new(void){return sp_PtrArray_new_scan(sp_gc_mark);}
|
|
351
|
+
static inline void sp_PtrArray_push(sp_PtrArray*a,void*v){if(a->len>=a->cap){sp_gc_hdr*h=(sp_gc_hdr*)((char*)a-sizeof(sp_gc_hdr));sp_gc_bytes-=sizeof(void*)*a->cap;h->size-=sizeof(void*)*a->cap;a->cap=a->cap*2+1;a->data=(void**)realloc(a->data,sizeof(void*)*a->cap);h->size+=sizeof(void*)*a->cap;sp_gc_bytes+=sizeof(void*)*a->cap;}a->data[a->len++]=v;}
|
|
352
|
+
static inline void*sp_PtrArray_get(sp_PtrArray*a,mrb_int i){if(i<0)i+=a->len;return a->data[i];}
|
|
353
|
+
static inline void sp_PtrArray_set(sp_PtrArray*a,mrb_int i,void*v){if(i<0)i+=a->len;a->data[i]=v;}
|
|
354
|
+
static inline mrb_int sp_PtrArray_length(sp_PtrArray*a){return a->len;}
|
|
355
|
+
static inline mrb_bool sp_PtrArray_empty(sp_PtrArray*a){return a->len==0;}
|
|
356
|
+
static void sp_PtrArray_reverse_bang(sp_PtrArray*a){for(mrb_int i=0,j=a->len-1;i<j;i++,j--){void*t=a->data[i];a->data[i]=a->data[j];a->data[j]=t;}}
|
|
357
|
+
static void sp_PtrArray_rotate_bang(sp_PtrArray*a,mrb_int n){if(a->len<=0)return;n=((n%a->len)+a->len)%a->len;if(n==0)return;void**tmp=(void**)malloc(sizeof(void*)*a->len);for(mrb_int i=0;i<a->len;i++)tmp[i]=a->data[(i+n)%a->len];for(mrb_int i=0;i<a->len;i++)a->data[i]=tmp[i];free(tmp);}
|
|
358
|
+
static sp_PtrArray*sp_PtrArray_dup(sp_PtrArray*a){sp_PtrArray*b=sp_PtrArray_new_scan(a->scan_elem);for(mrb_int i=0;i<a->len;i++)sp_PtrArray_push(b,a->data[i]);return b;}
|
|
359
|
+
static sp_PtrArray*sp_PtrArray_slice(sp_PtrArray*a,mrb_int start,mrb_int len){if(start<0)start+=a->len;if(start<0)start=0;sp_PtrArray*b=sp_PtrArray_new_scan(a->scan_elem);if(start>=a->len||len<=0)return b;if(start+len>a->len)len=a->len-start;for(mrb_int i=0;i<len;i++)sp_PtrArray_push(b,a->data[start+i]);return b;}
|
|
360
|
+
static void sp_PtrArray_shuffle_bang(sp_PtrArray*a){for(mrb_int i=a->len-1;i>0;i--){mrb_int j=(mrb_int)(rand()%(i+1));void*t=a->data[i];a->data[i]=a->data[j];a->data[j]=t;}}
|
|
361
|
+
static sp_PtrArray*sp_PtrArray_shuffle(sp_PtrArray*a){sp_PtrArray*b=sp_PtrArray_dup(a);sp_PtrArray_shuffle_bang(b);return b;}
|
|
362
|
+
|
|
363
|
+
/* Small-array optimization: keep the first SP_STRARR_INLINE elements
|
|
364
|
+
* inside the struct so empty/short StrArrays skip the data malloc.
|
|
365
|
+
* Common idiom "".split(",") and most class-metadata lists stay small.
|
|
366
|
+
* data == inline_data is the discriminator for "still on inline storage". */
|
|
367
|
+
#define SP_STRARR_INLINE 4
|
|
368
|
+
typedef struct{const char**data;mrb_int len;mrb_int cap;const char*inline_data[SP_STRARR_INLINE];}sp_StrArray;
|
|
369
|
+
static void sp_StrArray_fin(void*p){sp_StrArray*a=(sp_StrArray*)p;if(a->data!=a->inline_data){sp_gc_hdr*h=(sp_gc_hdr*)((char*)a-sizeof(sp_gc_hdr));sp_gc_bytes-=sizeof(const char*)*a->cap;h->size-=sizeof(const char*)*a->cap;free(a->data);}}
|
|
370
|
+
static void sp_StrArray_scan(void*p){sp_StrArray*a=(sp_StrArray*)p;for(mrb_int i=0;i<a->len;i++)sp_mark_string(a->data[i]);}
|
|
371
|
+
static sp_StrArray*sp_StrArray_new(void){sp_StrArray*a=(sp_StrArray*)sp_gc_alloc(sizeof(sp_StrArray),sp_StrArray_fin,sp_StrArray_scan);a->cap=SP_STRARR_INLINE;a->data=a->inline_data;a->len=0;return a;}
|
|
372
|
+
static inline void sp_StrArray_push(sp_StrArray*a,const char*v){if(a->len>=a->cap){sp_gc_hdr*h=(sp_gc_hdr*)((char*)a-sizeof(sp_gc_hdr));mrb_int nc=a->cap*2+1;if(a->data==a->inline_data){const char**nd=(const char**)malloc(sizeof(const char*)*nc);memcpy(nd,a->data,sizeof(const char*)*a->len);a->data=nd;}else{sp_gc_bytes-=sizeof(const char*)*a->cap;h->size-=sizeof(const char*)*a->cap;a->data=(const char**)realloc(a->data,sizeof(const char*)*nc);}a->cap=nc;h->size+=sizeof(const char*)*a->cap;sp_gc_bytes+=sizeof(const char*)*a->cap;}a->data[a->len++]=v;}
|
|
373
|
+
static void sp_StrArray_replace(sp_StrArray*dst,sp_StrArray*src){dst->len=0;if(src->len>dst->cap){sp_gc_hdr*h=(sp_gc_hdr*)((char*)dst-sizeof(sp_gc_hdr));void*nd;if(dst->data==dst->inline_data){nd=malloc(sizeof(const char*)*src->len);if(!nd){perror("malloc");exit(1);}}else{sp_gc_bytes-=sizeof(const char*)*dst->cap;h->size-=sizeof(const char*)*dst->cap;nd=realloc(dst->data,sizeof(const char*)*src->len);if(!nd){perror("realloc");exit(1);}}dst->data=(const char**)nd;dst->cap=src->len;h->size+=sizeof(const char*)*dst->cap;sp_gc_bytes+=sizeof(const char*)*dst->cap;}memcpy(dst->data,src->data,sizeof(const char*)*src->len);dst->len=src->len;}
|
|
374
|
+
static const char*sp_StrArray_pop(sp_StrArray*a){return a->data[--a->len];}
|
|
375
|
+
static inline mrb_int sp_StrArray_length(sp_StrArray*a){return a->len;}
|
|
376
|
+
static inline mrb_bool sp_StrArray_empty(sp_StrArray*a){return a->len==0;}
|
|
377
|
+
static inline const char*sp_StrArray_get(sp_StrArray*a,mrb_int i){if(i<0)i+=a->len;return a->data[i];}
|
|
378
|
+
/* a[start, len] / a[start..end] for StrArray. Same negative-start and
|
|
379
|
+
* length-clamping semantics as sp_IntArray_slice. Out-of-bounds start
|
|
380
|
+
* returns an empty StrArray (we don't have a nullable form). */
|
|
381
|
+
static sp_StrArray*sp_StrArray_slice(sp_StrArray*a,mrb_int start,mrb_int len){if(start<0)start+=a->len;if(start<0)start=0;sp_StrArray*b=sp_StrArray_new();if(start>=a->len||len<=0)return b;if(start+len>a->len)len=a->len-start;for(mrb_int i=0;i<len;i++)sp_StrArray_push(b,a->data[start+i]);return b;}
|
|
382
|
+
static inline void sp_StrArray_set(sp_StrArray*a,mrb_int i,const char*v){if(i<0)i+=a->len;while(i>=a->len)sp_StrArray_push(a,"");a->data[i]=v;}
|
|
383
|
+
static void sp_StrArray_reverse_bang(sp_StrArray*a){for(mrb_int i=0,j=a->len-1;i<j;i++,j--){const char*t=a->data[i];a->data[i]=a->data[j];a->data[j]=t;}}
|
|
384
|
+
static void sp_StrArray_rotate_bang(sp_StrArray*a,mrb_int n){if(a->len<=0)return;n=((n%a->len)+a->len)%a->len;if(n==0)return;const char**tmp=(const char**)malloc(sizeof(const char*)*a->len);for(mrb_int i=0;i<a->len;i++)tmp[i]=a->data[(i+n)%a->len];for(mrb_int i=0;i<a->len;i++)a->data[i]=tmp[i];free(tmp);}
|
|
385
|
+
static int _sp_str_cmp(const void*a,const void*b){return strcmp(*(const char*const*)a,*(const char*const*)b);}
|
|
386
|
+
static void sp_StrArray_sort_bang(sp_StrArray*a){qsort(a->data,a->len,sizeof(const char*),_sp_str_cmp);}
|
|
387
|
+
static const char*sp_StrArray_join(sp_StrArray*a,const char*sep){size_t sl=strlen(sep),cap=256;char*buf=(char*)malloc(cap);size_t len=0;for(mrb_int i=0;i<a->len;i++){if(i>0){if(len+sl>=cap){cap*=2;buf=(char*)realloc(buf,cap);}memcpy(buf+len,sep,sl);len+=sl;}size_t el=strlen(a->data[i]);if(len+el>=cap){cap=(len+el)*2+1;buf=(char*)realloc(buf,cap);}memcpy(buf+len,a->data[i],el);len+=el;}buf[len]=0;char*r=sp_str_alloc(len);memcpy(r,buf,len);free(buf);return r;}
|
|
388
|
+
static mrb_bool sp_StrArray_include(sp_StrArray*a,const char*v){for(mrb_int i=0;i<a->len;i++)if(strcmp(a->data[i],v)==0)return TRUE;return FALSE;}
|
|
389
|
+
static sp_StrArray*sp_StrArray_intersect(sp_StrArray*a,sp_StrArray*b){sp_StrArray*r=sp_StrArray_new();for(mrb_int i=0;i<a->len;i++){const char*v=a->data[i];if(sp_StrArray_include(b,v)&&!sp_StrArray_include(r,v))sp_StrArray_push(r,v);}return r;}
|
|
390
|
+
static sp_StrArray*sp_StrArray_union(sp_StrArray*a,sp_StrArray*b){sp_StrArray*r=sp_StrArray_new();for(mrb_int i=0;i<a->len;i++){const char*v=a->data[i];if(!sp_StrArray_include(r,v))sp_StrArray_push(r,v);}for(mrb_int i=0;i<b->len;i++){const char*v=b->data[i];if(!sp_StrArray_include(r,v))sp_StrArray_push(r,v);}return r;}
|
|
391
|
+
static sp_StrArray*sp_StrArray_difference(sp_StrArray*a,sp_StrArray*b){sp_StrArray*r=sp_StrArray_new();for(mrb_int i=0;i<a->len;i++){const char*v=a->data[i];if(!sp_StrArray_include(b,v))sp_StrArray_push(r,v);}return r;}
|
|
392
|
+
static mrb_int sp_StrArray_index(sp_StrArray*a,const char*v){for(mrb_int i=0;i<a->len;i++)if(strcmp(a->data[i],v)==0)return i;return -1;}
|
|
393
|
+
static mrb_int sp_StrArray_rindex(sp_StrArray*a,const char*v){for(mrb_int i=a->len-1;i>=0;i--)if(strcmp(a->data[i],v)==0)return i;return -1;}
|
|
394
|
+
static sp_StrArray*sp_StrArray_compact(sp_StrArray*a){sp_StrArray*r=sp_StrArray_new();for(mrb_int i=0;i<a->len;i++)if(a->data[i]!=NULL)sp_StrArray_push(r,a->data[i]);return r;}
|
|
395
|
+
static const char*sp_StrArray_delete_at(sp_StrArray*a,mrb_int i){if(i<0)i+=a->len;if(i<0||i>=a->len)return NULL;const char*v=a->data[i];for(mrb_int j=i;j<a->len-1;j++)a->data[j]=a->data[j+1];a->len--;return v;}
|
|
396
|
+
static const char*sp_StrArray_delete(sp_StrArray*a,const char*v){mrb_int w=0;const char*found=NULL;for(mrb_int i=0;i<a->len;i++){if(strcmp(a->data[i],v)!=0){a->data[w]=a->data[i];w++;}else{found=a->data[i];}}a->len=w;return found;}
|
|
397
|
+
static void sp_StrArray_insert(sp_StrArray*a,mrb_int i,const char*v){if(i<0)i+=a->len+1;sp_StrArray_push(a,"");for(mrb_int j=a->len-1;j>i;j--)a->data[j]=a->data[j-1];a->data[i]=v;}
|
|
398
|
+
static void sp_StrArray_shuffle_bang(sp_StrArray*a){for(mrb_int i=a->len-1;i>0;i--){mrb_int j=(mrb_int)(rand()%(i+1));const char*t=a->data[i];a->data[i]=a->data[j];a->data[j]=t;}}
|
|
399
|
+
static sp_StrArray*sp_StrArray_shuffle(sp_StrArray*a){sp_StrArray*r=sp_StrArray_new();sp_StrArray_replace(r,a);sp_StrArray_shuffle_bang(r);return r;}
|
|
400
|
+
|
|
401
|
+
static inline uint64_t sp_str_hash(const char*s){uint64_t h=14695981039346656037ULL;while(*s){h^=(unsigned char)*s++;h*=1099511628211ULL;}return h;}
|
|
402
|
+
typedef struct{const char**keys;mrb_int*vals;const char**order;mrb_int len;mrb_int cap;mrb_int mask;}sp_StrIntHash;
|
|
403
|
+
static void sp_StrIntHash_fin(void*p){sp_StrIntHash*h=(sp_StrIntHash*)p;free(h->keys);free(h->vals);free(h->order);}
|
|
404
|
+
static void sp_StrIntHash_scan(void*p){sp_StrIntHash*h=(sp_StrIntHash*)p;for(mrb_int i=0;i<h->cap;i++){if(h->keys[i])sp_mark_string(h->keys[i]);}}
|
|
405
|
+
static sp_StrIntHash*sp_StrIntHash_new(void){sp_StrIntHash*h=(sp_StrIntHash*)sp_gc_alloc(sizeof(sp_StrIntHash),sp_StrIntHash_fin,sp_StrIntHash_scan);h->cap=16;h->mask=15;h->keys=(const char**)calloc(h->cap,sizeof(const char*));h->vals=(mrb_int*)calloc(h->cap,sizeof(mrb_int));h->order=(const char**)malloc(sizeof(const char*)*h->cap);h->len=0;return h;}
|
|
406
|
+
static void sp_StrIntHash_grow(sp_StrIntHash*h){mrb_int oc=h->cap;const char**ok=h->keys;mrb_int*ov=h->vals;h->cap*=2;h->mask=h->cap-1;h->keys=(const char**)calloc(h->cap,sizeof(const char*));h->vals=(mrb_int*)calloc(h->cap,sizeof(mrb_int));h->order=(const char**)realloc(h->order,sizeof(const char*)*h->cap);mrb_int ol=h->len;h->len=0;for(mrb_int i=0;i<oc;i++){if(ok[i]){mrb_int idx=(mrb_int)(sp_str_hash(ok[i])&h->mask);while(h->keys[idx])idx=(idx+1)&h->mask;h->keys[idx]=ok[i];h->vals[idx]=ov[i];h->len++;}}free(ok);free(ov);}
|
|
407
|
+
static mrb_int sp_StrIntHash_get(sp_StrIntHash*h,const char*k){mrb_int idx=(mrb_int)(sp_str_hash(k)&h->mask);while(h->keys[idx]){if(strcmp(h->keys[idx],k)==0)return h->vals[idx];idx=(idx+1)&h->mask;}return 0;}
|
|
408
|
+
static void sp_StrIntHash_set(sp_StrIntHash*h,const char*k,mrb_int v){if(h->len*2>=h->cap)sp_StrIntHash_grow(h);mrb_int idx=(mrb_int)(sp_str_hash(k)&h->mask);while(h->keys[idx]){if(strcmp(h->keys[idx],k)==0){h->vals[idx]=v;return;}idx=(idx+1)&h->mask;}h->keys[idx]=k;h->vals[idx]=v;h->order[h->len]=k;h->len++;}
|
|
409
|
+
static mrb_bool sp_StrIntHash_has_key(sp_StrIntHash*h,const char*k){mrb_int idx=(mrb_int)(sp_str_hash(k)&h->mask);while(h->keys[idx]){if(strcmp(h->keys[idx],k)==0)return TRUE;idx=(idx+1)&h->mask;}return FALSE;}
|
|
410
|
+
static mrb_int sp_StrIntHash_length(sp_StrIntHash*h){return h->len;}
|
|
411
|
+
static void sp_StrIntHash_delete(sp_StrIntHash*h,const char*k){mrb_int idx=(mrb_int)(sp_str_hash(k)&h->mask);while(h->keys[idx]){if(strcmp(h->keys[idx],k)==0){h->keys[idx]=NULL;h->vals[idx]=0;h->len--;mrb_int j=(idx+1)&h->mask;while(h->keys[j]){mrb_int nj=(mrb_int)(sp_str_hash(h->keys[j])&h->mask);if((j>idx&&(nj<=idx||nj>j))||(j<idx&&nj<=idx&&nj>j)){h->keys[idx]=h->keys[j];h->vals[idx]=h->vals[j];h->keys[j]=NULL;h->vals[j]=0;idx=j;}j=(j+1)&h->mask;}{mrb_int oi=0;while(oi<=h->len){if(strcmp(h->order[oi],k)==0){while(oi<h->len){h->order[oi]=h->order[oi+1];oi++;}break;}oi++;}}return;}idx=(idx+1)&h->mask;}}
|
|
412
|
+
static sp_StrArray*sp_StrIntHash_keys(sp_StrIntHash*h){sp_StrArray*a=sp_StrArray_new();for(mrb_int i=0;i<h->len;i++)sp_StrArray_push(a,h->order[i]);return a;}
|
|
413
|
+
static sp_IntArray*sp_StrIntHash_values(sp_StrIntHash*h){sp_IntArray*a=sp_IntArray_new();for(mrb_int i=0;i<h->len;i++)sp_IntArray_push(a,sp_StrIntHash_get(h,h->order[i]));return a;}
|
|
414
|
+
static sp_StrIntHash*sp_StrArray_tally(sp_StrArray*a){sp_StrIntHash*h=sp_StrIntHash_new();for(mrb_int i=0;i<a->len;i++){const char*k=a->data[i];mrb_int c=sp_StrIntHash_has_key(h,k)?sp_StrIntHash_get(h,k):0;sp_StrIntHash_set(h,k,c+1);}return h;}
|
|
415
|
+
static sp_StrIntHash*sp_StrIntHash_merge(sp_StrIntHash*a,sp_StrIntHash*b){sp_StrIntHash*r=sp_StrIntHash_new();for(mrb_int i=0;i<a->len;i++)sp_StrIntHash_set(r,a->order[i],sp_StrIntHash_get(a,a->order[i]));for(mrb_int i=0;i<b->len;i++)sp_StrIntHash_set(r,b->order[i],sp_StrIntHash_get(b,b->order[i]));return r;}
|
|
416
|
+
static void sp_StrIntHash_update(sp_StrIntHash*a,sp_StrIntHash*b){for(mrb_int i=0;i<b->len;i++)sp_StrIntHash_set(a,b->order[i],sp_StrIntHash_get(b,b->order[i]));}
|
|
417
|
+
|
|
418
|
+
typedef struct{const char**keys;const char**vals;const char**order;mrb_int len;mrb_int cap;mrb_int mask;}sp_StrStrHash;
|
|
419
|
+
static void sp_StrStrHash_fin(void*p){sp_StrStrHash*h=(sp_StrStrHash*)p;free(h->keys);free(h->vals);free(h->order);}
|
|
420
|
+
static void sp_StrStrHash_scan(void*p){sp_StrStrHash*h=(sp_StrStrHash*)p;for(mrb_int i=0;i<h->cap;i++){if(h->keys[i]){sp_mark_string(h->keys[i]);sp_mark_string(h->vals[i]);}}}
|
|
421
|
+
static sp_StrStrHash*sp_StrStrHash_new(void){sp_StrStrHash*h=(sp_StrStrHash*)sp_gc_alloc(sizeof(sp_StrStrHash),sp_StrStrHash_fin,sp_StrStrHash_scan);h->cap=16;h->mask=15;h->keys=(const char**)calloc(h->cap,sizeof(const char*));h->vals=(const char**)calloc(h->cap,sizeof(const char*));h->order=(const char**)malloc(sizeof(const char*)*h->cap);h->len=0;return h;}
|
|
422
|
+
static void sp_StrStrHash_grow(sp_StrStrHash*h){mrb_int oc=h->cap;const char**ok=h->keys;const char**ov=h->vals;h->cap*=2;h->mask=h->cap-1;h->keys=(const char**)calloc(h->cap,sizeof(const char*));h->vals=(const char**)calloc(h->cap,sizeof(const char*));h->order=(const char**)realloc(h->order,sizeof(const char*)*h->cap);h->len=0;for(mrb_int i=0;i<oc;i++){if(ok[i]){mrb_int idx=(mrb_int)(sp_str_hash(ok[i])&h->mask);while(h->keys[idx])idx=(idx+1)&h->mask;h->keys[idx]=ok[i];h->vals[idx]=ov[i];h->len++;}}free(ok);free(ov);}
|
|
423
|
+
static const char*sp_StrStrHash_get(sp_StrStrHash*h,const char*k){mrb_int idx=(mrb_int)(sp_str_hash(k)&h->mask);while(h->keys[idx]){if(strcmp(h->keys[idx],k)==0)return h->vals[idx];idx=(idx+1)&h->mask;}return"";}
|
|
424
|
+
static void sp_StrStrHash_set(sp_StrStrHash*h,const char*k,const char*v){if(h->len*2>=h->cap)sp_StrStrHash_grow(h);mrb_int idx=(mrb_int)(sp_str_hash(k)&h->mask);while(h->keys[idx]){if(strcmp(h->keys[idx],k)==0){h->vals[idx]=v;return;}idx=(idx+1)&h->mask;}h->keys[idx]=k;h->vals[idx]=v;h->order[h->len]=k;h->len++;}
|
|
425
|
+
static mrb_bool sp_StrStrHash_has_key(sp_StrStrHash*h,const char*k){mrb_int idx=(mrb_int)(sp_str_hash(k)&h->mask);while(h->keys[idx]){if(strcmp(h->keys[idx],k)==0)return TRUE;idx=(idx+1)&h->mask;}return FALSE;}
|
|
426
|
+
static mrb_int sp_StrStrHash_length(sp_StrStrHash*h){return h->len;}
|
|
427
|
+
static void sp_StrStrHash_delete(sp_StrStrHash*h,const char*k){mrb_int idx=(mrb_int)(sp_str_hash(k)&h->mask);while(h->keys[idx]){if(strcmp(h->keys[idx],k)==0){h->keys[idx]=NULL;h->vals[idx]=NULL;h->len--;mrb_int j=(idx+1)&h->mask;while(h->keys[j]){mrb_int nj=(mrb_int)(sp_str_hash(h->keys[j])&h->mask);if((j>idx&&(nj<=idx||nj>j))||(j<idx&&nj<=idx&&nj>j)){h->keys[idx]=h->keys[j];h->vals[idx]=h->vals[j];h->keys[j]=NULL;h->vals[j]=NULL;idx=j;}j=(j+1)&h->mask;}{mrb_int oi=0;while(oi<=h->len){if(strcmp(h->order[oi],k)==0){while(oi<h->len){h->order[oi]=h->order[oi+1];oi++;}break;}oi++;}}return;}idx=(idx+1)&h->mask;}}
|
|
428
|
+
static sp_StrArray*sp_StrStrHash_keys(sp_StrStrHash*h){sp_StrArray*a=sp_StrArray_new();for(mrb_int i=0;i<h->len;i++)sp_StrArray_push(a,h->order[i]);return a;}
|
|
429
|
+
static sp_StrArray*sp_StrStrHash_values(sp_StrStrHash*h){sp_StrArray*a=sp_StrArray_new();for(mrb_int i=0;i<h->len;i++)sp_StrArray_push(a,sp_StrStrHash_get(h,h->order[i]));return a;}
|
|
430
|
+
static sp_StrStrHash*sp_StrStrHash_invert(sp_StrStrHash*h){sp_StrStrHash*r=sp_StrStrHash_new();for(mrb_int i=0;i<h->len;i++){const char*k=h->order[i];sp_StrStrHash_set(r,sp_StrStrHash_get(h,k),k);}return r;}
|
|
431
|
+
static void sp_StrStrHash_update(sp_StrStrHash*a,sp_StrStrHash*b){for(mrb_int i=0;i<b->len;i++)sp_StrStrHash_set(a,b->order[i],sp_StrStrHash_get(b,b->order[i]));}
|
|
432
|
+
|
|
433
|
+
typedef struct{mrb_int*keys;const char**vals;mrb_int*order;mrb_bool*used;mrb_int len;mrb_int cap;mrb_int mask;}sp_IntStrHash;
|
|
434
|
+
static void sp_IntStrHash_fin(void*p){sp_IntStrHash*h=(sp_IntStrHash*)p;free(h->keys);free(h->vals);free(h->order);free(h->used);}
|
|
435
|
+
static void sp_IntStrHash_scan(void*p){sp_IntStrHash*h=(sp_IntStrHash*)p;for(mrb_int i=0;i<h->cap;i++)if(h->used[i])sp_mark_string(h->vals[i]);}
|
|
436
|
+
static sp_IntStrHash*sp_IntStrHash_new(void){sp_IntStrHash*h=(sp_IntStrHash*)sp_gc_alloc(sizeof(sp_IntStrHash),sp_IntStrHash_fin,sp_IntStrHash_scan);h->cap=16;h->mask=15;h->keys=(mrb_int*)calloc(h->cap,sizeof(mrb_int));h->vals=(const char**)calloc(h->cap,sizeof(const char*));h->order=(mrb_int*)malloc(sizeof(mrb_int)*h->cap);h->used=(mrb_bool*)calloc(h->cap,sizeof(mrb_bool));h->len=0;return h;}
|
|
437
|
+
static inline mrb_int _sp_istr_idx(mrb_int mask,mrb_int k){return(mrb_int)(((uint64_t)(unsigned long long)k*11400714819323198485ULL)&(uint64_t)mask);}
|
|
438
|
+
static void sp_IntStrHash_grow(sp_IntStrHash*h){mrb_int oc=h->cap,ol=h->len;mrb_int*ok=h->keys;const char**ov=h->vals;mrb_bool*ou=h->used;mrb_int*oo=h->order;h->cap*=2;h->mask=h->cap-1;h->keys=(mrb_int*)calloc(h->cap,sizeof(mrb_int));h->vals=(const char**)calloc(h->cap,sizeof(const char*));h->order=(mrb_int*)malloc(sizeof(mrb_int)*h->cap);h->used=(mrb_bool*)calloc(h->cap,sizeof(mrb_bool));h->len=ol;for(mrb_int i=0;i<oc;i++){if(!ou[i])continue;mrb_int k=ok[i];const char*v=ov[i];mrb_int di=_sp_istr_idx(h->mask,k);while(h->used[di])di=(di+1)&h->mask;h->used[di]=TRUE;h->keys[di]=k;h->vals[di]=v;}for(mrb_int i=0;i<ol;i++)h->order[i]=oo[i];free(ok);free(ov);free(ou);free(oo);}
|
|
439
|
+
static void sp_IntStrHash_set(sp_IntStrHash*h,mrb_int k,const char*v){if(h->len*2>=h->cap)sp_IntStrHash_grow(h);mrb_int idx=_sp_istr_idx(h->mask,k);while(h->used[idx]){if(h->keys[idx]==k){h->vals[idx]=v;return;}idx=(idx+1)&h->mask;}h->used[idx]=TRUE;h->keys[idx]=k;h->vals[idx]=v;h->order[h->len++]=k;}
|
|
440
|
+
static const char*sp_IntStrHash_get(sp_IntStrHash*h,mrb_int k){mrb_int idx=_sp_istr_idx(h->mask,k);while(h->used[idx]){if(h->keys[idx]==k)return h->vals[idx];idx=(idx+1)&h->mask;}return"";}
|
|
441
|
+
static mrb_bool sp_IntStrHash_has_key(sp_IntStrHash*h,mrb_int k){mrb_int idx=_sp_istr_idx(h->mask,k);while(h->used[idx]){if(h->keys[idx]==k)return TRUE;idx=(idx+1)&h->mask;}return FALSE;}
|
|
442
|
+
static mrb_int sp_IntStrHash_length(sp_IntStrHash*h){return h->len;}
|
|
443
|
+
static sp_IntArray*sp_IntStrHash_keys(sp_IntStrHash*h){sp_IntArray*a=sp_IntArray_new();for(mrb_int i=0;i<h->len;i++)sp_IntArray_push(a,h->order[i]);return a;}
|
|
444
|
+
static sp_StrArray*sp_IntStrHash_values(sp_IntStrHash*h){sp_StrArray*a=sp_StrArray_new();for(mrb_int i=0;i<h->len;i++)sp_StrArray_push(a,sp_IntStrHash_get(h,h->order[i]));return a;}
|
|
445
|
+
|
|
446
|
+
/* Reuse an existing StrArray for split, avoiding GC alloc.
|
|
447
|
+
Clears a->len and refills. Substring strings are still malloc'd. */
|
|
448
|
+
static void sp_str_split_into(sp_StrArray*a,const char*s,const char*sep){
|
|
449
|
+
a->len=0;if(*s==0)return;size_t sl=strlen(sep);
|
|
450
|
+
if(sl==0){const char*p=s;while(*p){int cn=sp_utf8_advance(p);char*c=sp_str_alloc_raw(cn+1);memcpy(c,p,cn);c[cn]=0;sp_StrArray_push(a,c);p+=cn;}return;}
|
|
451
|
+
const char*p=s;while(1){const char*f=strstr(p,sep);if(!f){char*r=sp_str_alloc_raw(strlen(p)+1);strcpy(r,p);sp_StrArray_push(a,r);break;}
|
|
452
|
+
size_t n=f-p;char*r=sp_str_alloc_raw(n+1);memcpy(r,p,n);r[n]=0;sp_StrArray_push(a,r);p=f+sl;}}
|
|
453
|
+
/* Extract the n-th field (0-based) from s split by sep, without
|
|
454
|
+
allocating a full StrArray. Returns a newly allocated string.
|
|
455
|
+
If the field doesn't exist, returns "". */
|
|
456
|
+
static const char*sp_str_field(const char*s,const char*sep,mrb_int n){
|
|
457
|
+
size_t sl=strlen(sep);mrb_int cur=0;const char*p=s;
|
|
458
|
+
if(sl==0)return("");
|
|
459
|
+
while(cur<n){const char*f=strstr(p,sep);if(!f)return("");p=f+sl;cur++;}
|
|
460
|
+
const char*end=strstr(p,sep);size_t len=end?((size_t)(end-p)):strlen(p);
|
|
461
|
+
char*r=sp_str_alloc_raw(len+1);memcpy(r,p,len);r[len]=0;return r;}
|
|
462
|
+
/* Count fields in s split by sep (without allocating). */
|
|
463
|
+
static mrb_int sp_str_field_count(const char*s,const char*sep){
|
|
464
|
+
if(*s==0)return 0;size_t sl=strlen(sep);if(sl==0)return(mrb_int)strlen(s);
|
|
465
|
+
mrb_int c=1;const char*p=s;while((p=strstr(p,sep))!=NULL){c++;p+=sl;}return c;}
|
|
466
|
+
static const char*sp_str_concat(const char*a,const char*b){size_t la=strlen(a),lb=strlen(b);char*r=sp_str_alloc(la+lb);memcpy(r,a,la);memcpy(r+la,b,lb);return r;}
|
|
467
|
+
static const char*sp_str_concat3(const char*a,const char*b,const char*c){size_t la=strlen(a),lb=strlen(b),lc=strlen(c);char*r=sp_str_alloc(la+lb+lc);memcpy(r,a,la);memcpy(r+la,b,lb);memcpy(r+la+lb,c,lc);return r;}
|
|
468
|
+
static const char*sp_str_concat4(const char*a,const char*b,const char*c,const char*d){size_t la=strlen(a),lb=strlen(b),lc=strlen(c),ld=strlen(d);char*r=sp_str_alloc(la+lb+lc+ld);memcpy(r,a,la);memcpy(r+la,b,lb);memcpy(r+la+lb,c,lc);memcpy(r+la+lb+lc,d,ld);return r;}
|
|
469
|
+
/* Concatenate N strings into a single GC-managed buffer. */
|
|
470
|
+
static const char*sp_str_concat_arr(const char *const *parts,int n){size_t total=0;for(int i=0;i<n;i++)total+=strlen(parts[i]);char*r=sp_str_alloc(total);char*p=r;for(int i=0;i<n;i++){size_t sl=strlen(parts[i]);memcpy(p,parts[i],sl);p+=sl;}return r;}
|
|
471
|
+
static const char*sp_int_to_s(mrb_int n){char*b=sp_str_alloc_raw(32);snprintf(b,32,"%lld",(long long)n);return b;}
|
|
472
|
+
static const char*sp_int_to_s_base(mrb_int n,mrb_int base){if(base<2||base>36)base=10;char*b=sp_str_alloc_raw(72);char tmp[72];int i=0;int neg=0;uint64_t u;if(n<0){neg=1;u=(uint64_t)(-(n+1))+1;}else{u=(uint64_t)n;}if(u==0){tmp[i++]='0';}else{while(u>0){mrb_int d=u%base;tmp[i++]=d<10?'0'+d:'a'+d-10;u/=base;}}int j=0;if(neg)b[j++]='-';while(i>0)b[j++]=tmp[--i];b[j]=0;return b;}
|
|
473
|
+
/* Float#to_s (Ruby semantics): produce the shortest decimal that
|
|
474
|
+
round-trips back to the same double, formatted per CRuby — fixed
|
|
475
|
+
point when the decimal exponent is in [-4, 15], scientific
|
|
476
|
+
(`d.ddde+NN`, two-digit zero-padded) otherwise. NaN, ±Infinity and
|
|
477
|
+
-0.0 match CRuby's spelling. Float#inspect is aliased. */
|
|
478
|
+
static const char*sp_float_to_s(mrb_float f){
|
|
479
|
+
if(f!=f){char*r=sp_str_alloc_raw(4);r[0]='N';r[1]='a';r[2]='N';r[3]=0;return r;}
|
|
480
|
+
if(f==HUGE_VAL||f==-HUGE_VAL){if(f<0){char*r=sp_str_alloc_raw(10);memcpy(r,"-Infinity",10);return r;}char*r=sp_str_alloc_raw(9);memcpy(r,"Infinity",9);return r;}
|
|
481
|
+
if(f==0.0){if(signbit(f)){char*r=sp_str_alloc_raw(5);memcpy(r,"-0.0",5);return r;}char*r=sp_str_alloc_raw(4);memcpy(r,"0.0",4);return r;}
|
|
482
|
+
char tmp[64];int p;
|
|
483
|
+
for(p=0;p<=17;p++){snprintf(tmp,sizeof(tmp),"%.*e",p,(double)f);if(strtod(tmp,NULL)==f)break;}
|
|
484
|
+
int neg=(tmp[0]=='-')?1:0;const char*s=tmp+neg;char digits[32];int dlen=0;
|
|
485
|
+
digits[dlen++]=*s++;
|
|
486
|
+
if(*s=='.'){s++;while(*s&&*s!='e'&&*s!='E')digits[dlen++]=*s++;}
|
|
487
|
+
while(*s&&*s!='e'&&*s!='E')s++;
|
|
488
|
+
int exp_val=(*s)?atoi(s+1):0;int decpt=exp_val+1;
|
|
489
|
+
char*out=sp_str_alloc_raw(64);int o=0;
|
|
490
|
+
if(neg)out[o++]='-';
|
|
491
|
+
if(decpt>0&&decpt<=15){
|
|
492
|
+
if(decpt<dlen){memcpy(out+o,digits,decpt);o+=decpt;out[o++]='.';memcpy(out+o,digits+decpt,dlen-decpt);o+=(dlen-decpt);}
|
|
493
|
+
else{memcpy(out+o,digits,dlen);o+=dlen;for(int i=dlen;i<decpt;i++)out[o++]='0';out[o++]='.';out[o++]='0';}
|
|
494
|
+
}else if(decpt<=0&&decpt>-4){
|
|
495
|
+
out[o++]='0';out[o++]='.';for(int i=decpt;i<0;i++)out[o++]='0';memcpy(out+o,digits,dlen);o+=dlen;
|
|
496
|
+
}else{
|
|
497
|
+
out[o++]=digits[0];out[o++]='.';
|
|
498
|
+
if(dlen==1)out[o++]='0';else{memcpy(out+o,digits+1,dlen-1);o+=(dlen-1);}
|
|
499
|
+
out[o++]='e';int e=decpt-1;
|
|
500
|
+
if(e>=0)out[o++]='+';else{out[o++]='-';e=-e;}
|
|
501
|
+
if(e<10){out[o++]='0';out[o++]=(char)('0'+e);}else o+=snprintf(out+o,16,"%d",e);
|
|
502
|
+
}
|
|
503
|
+
out[o]=0;return out;
|
|
504
|
+
}
|
|
505
|
+
#define sp_float_inspect sp_float_to_s
|
|
506
|
+
/* String#inspect: wrap in double quotes and escape \, ", \n, \t, \r,
|
|
507
|
+
plus any non-printable byte as \xNN. Output is always ASCII-safe. */
|
|
508
|
+
static const char*sp_str_inspect(const char*s){if(!s){char*r=sp_str_alloc_raw(4);r[0]='n';r[1]='i';r[2]='l';r[3]=0;return r;}size_t sl=strlen(s);size_t cap=sl*4+3;char*r=sp_str_alloc_raw(cap);size_t o=0;r[o++]='"';for(size_t i=0;i<sl;i++){unsigned char c=(unsigned char)s[i];if(c=='\\'||c=='"'){r[o++]='\\';r[o++]=c;}else if(c=='\n'){r[o++]='\\';r[o++]='n';}else if(c=='\t'){r[o++]='\\';r[o++]='t';}else if(c=='\r'){r[o++]='\\';r[o++]='r';}else if(c<0x20||c==0x7f){snprintf(r+o,5,"\\x%02X",c);o+=4;}else{r[o++]=(char)c;}}r[o++]='"';r[o]=0;return r;}
|
|
509
|
+
static const char*sp_str_upcase(const char*s){size_t l=strlen(s);char*r=sp_str_alloc_raw(l+1);for(size_t i=0;i<=l;i++)r[i]=toupper((unsigned char)s[i]);return r;}
|
|
510
|
+
static const char*sp_str_downcase(const char*s){size_t l=strlen(s);char*r=sp_str_alloc_raw(l+1);for(size_t i=0;i<=l;i++)r[i]=tolower((unsigned char)s[i]);return r;}
|
|
511
|
+
static const char*sp_str_swapcase(const char*s){size_t l=strlen(s);char*r=sp_str_alloc_raw(l+1);for(size_t i=0;i<=l;i++){unsigned char c=(unsigned char)s[i];if(isupper(c))r[i]=tolower(c);else if(islower(c))r[i]=toupper(c);else r[i]=s[i];}return r;}
|
|
512
|
+
static const char*sp_str_delete_prefix(const char*s,const char*p){size_t sl=strlen(s),pl=strlen(p);if(pl<=sl&&memcmp(s,p,pl)==0){char*r=sp_str_alloc_raw(sl-pl+1);memcpy(r,s+pl,sl-pl+1);return r;}char*r=sp_str_alloc_raw(sl+1);memcpy(r,s,sl+1);return r;}
|
|
513
|
+
static const char*sp_str_substr(const char*s,mrb_int start,mrb_int len){if(len<=0){char*r=sp_str_alloc_raw(1);r[0]=0;return r;}char*r=sp_str_alloc_raw(len+1);memcpy(r,s+start,len);r[len]=0;return r;}
|
|
514
|
+
static const char*sp_str_delete_suffix(const char*s,const char*p){size_t sl=strlen(s),pl=strlen(p);if(pl<=sl&&memcmp(s+sl-pl,p,pl)==0){char*r=sp_str_alloc_raw(sl-pl+1);memcpy(r,s,sl-pl);r[sl-pl]=0;return r;}char*r=sp_str_alloc_raw(sl+1);memcpy(r,s,sl+1);return r;}
|
|
515
|
+
static const char*sp_str_succ(const char*s){size_t l=strlen(s);if(l==0){char*r=sp_str_alloc_raw(1);r[0]=0;return r;}/* Find start of last codepoint */size_t lc=l-1;while(lc>0&&((unsigned char)s[lc]&0xC0)==0x80)lc--;if((unsigned char)s[lc]>=0x80){/* Multibyte tail: increment its codepoint */uint32_t cp;sp_utf8_decode(s+lc,&cp);cp++;char enc[4];int el=sp_utf8_encode(cp,enc);char*r=sp_str_alloc_raw(lc+el+1);memcpy(r,s,lc);memcpy(r+lc,enc,el);r[lc+el]=0;return r;}/* ASCII tail: existing carry logic */char*r=sp_str_alloc_raw(l+2);memcpy(r,s,l+1);mrb_int i=(mrb_int)l-1;while(i>=0){unsigned char c=(unsigned char)r[i];if(c>='0'&&c<'9'){r[i]=c+1;return r;}if(c=='9'){r[i]='0';i--;continue;}if(c>='a'&&c<'z'){r[i]=c+1;return r;}if(c=='z'){r[i]='a';i--;continue;}if(c>='A'&&c<'Z'){r[i]=c+1;return r;}if(c=='Z'){r[i]='A';i--;continue;}r[i]=c+1;return r;}memmove(r+1,r,l+1);if(r[1]=='0')r[0]='1';else if(r[1]=='a')r[0]='a';else if(r[1]=='A')r[0]='A';else r[0]=r[1];return r;}
|
|
516
|
+
static const char*sp_gets(void){char buf[4096];if(!fgets(buf,sizeof(buf),stdin))return NULL;size_t l=strlen(buf);char*r=sp_str_alloc_raw(l+1);memcpy(r,buf,l+1);return r;}
|
|
517
|
+
static sp_StrArray*sp_readlines(void){sp_StrArray*a=sp_StrArray_new();char buf[4096];while(fgets(buf,sizeof(buf),stdin)){size_t l=strlen(buf);char*r=sp_str_alloc_raw(l+1);memcpy(r,buf,l+1);sp_StrArray_push(a,r);}return a;}
|
|
518
|
+
static const char*sp_str_strip(const char*s){while(*s&&isspace((unsigned char)*s))s++;size_t l=strlen(s);while(l>0&&isspace((unsigned char)s[l-1]))l--;char*r=sp_str_alloc_raw(l+1);memcpy(r,s,l);r[l]=0;return r;}
|
|
519
|
+
static const char*sp_str_chomp(const char*s){size_t l=strlen(s);while(l>0&&(s[l-1]=='\n'||s[l-1]=='\r'))l--;char*r=sp_str_alloc_raw(l+1);memcpy(r,s,l);r[l]=0;return r;}
|
|
520
|
+
static const char*sp_str_chop(const char*s){size_t l=strlen(s);if(l>0){if(l>=2&&s[l-2]=='\r'&&s[l-1]=='\n')l-=2;else l--;}char*r=sp_str_alloc_raw(l+1);memcpy(r,s,l);r[l]=0;return r;}
|
|
521
|
+
static mrb_bool sp_str_include(const char*s,const char*sub){return strstr(s,sub)!=NULL;}
|
|
522
|
+
static mrb_bool sp_str_start_with(const char*s,const char*p){return strncmp(s,p,strlen(p))==0;}
|
|
523
|
+
static mrb_bool sp_str_end_with(const char*s,const char*suf){size_t ls=strlen(s),lsuf=strlen(suf);if(lsuf>ls)return FALSE;return strcmp(s+ls-lsuf,suf)==0;}
|
|
524
|
+
static sp_StrArray*sp_str_split(const char*s,const char*sep){sp_StrArray*a=sp_StrArray_new();if(*s==0)return a;size_t sl=strlen(sep);if(sl==0){const char*p=s;while(*p){int cn=sp_utf8_advance(p);char*c=sp_str_alloc_raw(cn+1);memcpy(c,p,cn);c[cn]=0;sp_StrArray_push(a,c);p+=cn;}return a;}const char*p=s;while(1){const char*f=strstr(p,sep);if(!f){char*r=sp_str_alloc_raw(strlen(p)+1);strcpy(r,p);sp_StrArray_push(a,r);break;}size_t n=f-p;char*r=sp_str_alloc_raw(n+1);memcpy(r,p,n);r[n]=0;sp_StrArray_push(a,r);p=f+sl;}return a;}
|
|
525
|
+
/* String#lines: split on \n but PRESERVE the trailing newline on each
|
|
526
|
+
line (CRuby semantics). The last line keeps its terminator if present;
|
|
527
|
+
if absent, it just stops there. Empty string returns an empty array.
|
|
528
|
+
`end` is computed once at entry so a string with no newlines avoids
|
|
529
|
+
a redundant strlen call on the trailing piece. */
|
|
530
|
+
static sp_StrArray*sp_str_lines(const char*s){sp_StrArray*a=sp_StrArray_new();if(*s==0)return a;const char*end=s+strlen(s);const char*p=s;while(p<end){const char*nl=strchr(p,'\n');size_t n=nl?(size_t)(nl-p+1):(size_t)(end-p);char*r=sp_str_alloc_raw(n+1);memcpy(r,p,n);r[n]=0;sp_StrArray_push(a,r);if(!nl)break;p=nl+1;}return a;}
|
|
531
|
+
static const char*sp_str_gsub(const char*s,const char*pat,const char*rep){size_t pl=strlen(pat),rl=strlen(rep),sl=strlen(s);if(pl==0)return s;size_t cap=sl*2+1;char*out=(char*)malloc(cap);size_t ol=0;const char*p=s;while(*p){const char*f=strstr(p,pat);if(!f){size_t n=strlen(p);if(ol+n>=cap){cap=(ol+n)*2+1;out=(char*)realloc(out,cap);}memcpy(out+ol,p,n);ol+=n;break;}size_t n=f-p;if(ol+n+rl>=cap){cap=(ol+n+rl)*2+1;out=(char*)realloc(out,cap);}memcpy(out+ol,p,n);ol+=n;memcpy(out+ol,rep,rl);ol+=rl;p=f+pl;}out[ol]=0;return out;}
|
|
532
|
+
/* Returns a *character* offset (codepoint index), not a byte offset. */
|
|
533
|
+
static mrb_int sp_str_index(const char*s,const char*sub){const char*f=strstr(s,sub);if(!f)return -1;mrb_int n=0;const char*p=s;while(p<f){p+=sp_utf8_advance(p);n++;}return n;}
|
|
534
|
+
static mrb_int sp_str_rindex(const char*s,const char*sub){size_t sl=strlen(sub);if(sl==0)return sp_str_length(s);const char*last=NULL;const char*p=s;while((p=strstr(p,sub))){last=p;p++;}if(!last)return -1;mrb_int n=0;const char*q=s;while(q<last){q+=sp_utf8_advance(q);n++;}return n;}
|
|
535
|
+
static char sp_char_cache[256][3];
|
|
536
|
+
static int sp_char_cache_init = 0;
|
|
537
|
+
/* start/len are codepoint indices/counts. */
|
|
538
|
+
static const char*sp_str_sub_range(const char*s,mrb_int start,mrb_int len){mrb_int cl=sp_str_length(s);if(start<0)start+=cl;if(start<0)start=0;if(start>=cl||len<=0){return &("\xff" "")[1];}if(start+len>cl)len=cl-start;size_t boff=sp_utf8_byte_offset(s,start);size_t bend=sp_utf8_byte_offset(s+boff,len)+boff;size_t blen=bend-boff;if(len==1&&blen==1){unsigned char c=(unsigned char)s[boff];if(!sp_char_cache_init){for(int i=0;i<256;i++){sp_char_cache[i][0]=(char)0xff;sp_char_cache[i][1]=(char)i;sp_char_cache[i][2]=0;}sp_char_cache_init=1;}return &sp_char_cache[c][1];}char*r=sp_str_alloc_raw(blen+1);memcpy(r,s+boff,blen);r[blen]=0;return r;}
|
|
539
|
+
/* Char-indexed variant; the second arg used to be a hoisted byte length, now a
|
|
540
|
+
hoisted codepoint count. We don't need it for correctness, but keeping the
|
|
541
|
+
ABI lets callers pass it without a wrapper. */
|
|
542
|
+
static const char*sp_str_sub_range_len(const char*s,mrb_int cl,mrb_int start,mrb_int len){if(start<0)start+=cl;if(start<0)start=0;if(start>=cl||len<=0){return &("\xff" "")[1];}if(start+len>cl)len=cl-start;size_t boff=sp_utf8_byte_offset(s,start);size_t bend=sp_utf8_byte_offset(s+boff,len)+boff;size_t blen=bend-boff;if(len==1&&blen==1){unsigned char c=(unsigned char)s[boff];if(!sp_char_cache_init){for(int i=0;i<256;i++){sp_char_cache[i][0]=(char)0xff;sp_char_cache[i][1]=(char)i;sp_char_cache[i][2]=0;}sp_char_cache_init=1;}return &sp_char_cache[c][1];}char*r=sp_str_alloc_raw(blen+1);memcpy(r,s+boff,blen);r[blen]=0;return r;}
|
|
543
|
+
static const char*sp_sprintf(const char*fmt,...){char _sp_tmp[4096];va_list ap;va_start(ap,fmt);int _sp_n=vsnprintf(_sp_tmp,sizeof(_sp_tmp),fmt,ap);va_end(ap);if(_sp_n<0)_sp_n=0;if(_sp_n>=(int)sizeof(_sp_tmp))_sp_n=(int)sizeof(_sp_tmp)-1;char*b=sp_str_alloc(_sp_n);memcpy(b,_sp_tmp,_sp_n);return b;}
|
|
544
|
+
/* Use a temp pointer for realloc so the original buffer is not leaked
|
|
545
|
+
on allocation failure. Match the perror+exit pattern used elsewhere
|
|
546
|
+
(see sp_IntArray_replace) instead of returning a partial result. */
|
|
547
|
+
static const char*sp_str_format_strarr(const char*fmt,sp_StrArray*a){size_t cap=strlen(fmt)+64;char*buf=(char*)malloc(cap);if(!buf){perror("malloc");exit(1);}size_t out=0;mrb_int idx=0;const char*p=fmt;while(*p){if(*p=='%'){if(p[1]=='s'){const char*s=(idx<a->len)?a->data[idx]:"";size_t sl=strlen(s);if(out+sl>=cap){size_t nc=(out+sl)*2+1;char*nb=(char*)realloc(buf,nc);if(!nb){free(buf);perror("realloc");exit(1);}buf=nb;cap=nc;}memcpy(buf+out,s,sl);out+=sl;idx++;p+=2;}else if(p[1]=='%'){if(out+1>=cap){size_t nc=cap*2;char*nb=(char*)realloc(buf,nc);if(!nb){free(buf);perror("realloc");exit(1);}buf=nb;cap=nc;}buf[out++]='%';p+=2;}else{if(out+1>=cap){size_t nc=cap*2;char*nb=(char*)realloc(buf,nc);if(!nb){free(buf);perror("realloc");exit(1);}buf=nb;cap=nc;}buf[out++]=*p++;}}else{if(out+1>=cap){size_t nc=cap*2;char*nb=(char*)realloc(buf,nc);if(!nb){free(buf);perror("realloc");exit(1);}buf=nb;cap=nc;}buf[out++]=*p++;}}buf[out]=0;char*r=sp_str_alloc(out);memcpy(r,buf,out);free(buf);return r;}
|
|
548
|
+
static const char*sp_str_reverse(const char*s){size_t bl=strlen(s);char*r=sp_str_alloc_raw(bl+1);size_t end=bl;const char*p=s;while(*p){int cn=sp_utf8_advance(p);end-=cn;memcpy(r+end,p,cn);p+=cn;}r[bl]=0;return r;}
|
|
549
|
+
static const char*sp_str_sub(const char*s,const char*pat,const char*rep){const char*f=strstr(s,pat);if(!f)return s;size_t pl=strlen(pat),rl=strlen(rep),sl=strlen(s);char*r=sp_str_alloc_raw(sl-pl+rl+1);size_t n=f-s;memcpy(r,s,n);memcpy(r+n,rep,rl);memcpy(r+n+rl,f+pl,sl-n-pl+1);return r;}
|
|
550
|
+
static const char*sp_str_capitalize(const char*s){size_t l=strlen(s);char*r=sp_str_alloc_raw(l+1);for(size_t i=0;i<=l;i++)r[i]=tolower((unsigned char)s[i]);if(l>0)r[0]=toupper((unsigned char)r[0]);return r;}
|
|
551
|
+
static mrb_int sp_str_count(const char*s,const char*chars){size_t setn;uint32_t*set=sp_utf8_decode_all(chars,&setn);mrb_int c=0;const char*p=s;while(*p){uint32_t cp;p+=sp_utf8_decode(p,&cp);if(sp_utf8_set_has(set,setn,cp))c++;}free(set);return c;}
|
|
552
|
+
static const char*sp_str_repeat(const char*s,mrb_int n){if(n<=0)return"";size_t l=strlen(s);char*r=sp_str_alloc_raw(l*n+1);for(mrb_int i=0;i<n;i++)memcpy(r+l*i,s,l);r[l*n]=0;return r;}
|
|
553
|
+
static sp_IntArray*sp_str_bytes(const char*s){sp_IntArray*a=sp_IntArray_new();for(size_t i=0;s[i];i++)sp_IntArray_push(a,(mrb_int)(unsigned char)s[i]);return a;}
|
|
554
|
+
static const char*sp_str_tr(const char*s,const char*from,const char*to){size_t fn,tn;uint32_t*fcps=sp_utf8_decode_all(from,&fn);uint32_t*tcps=sp_utf8_decode_all(to,&tn);size_t bl=strlen(s);size_t cap=bl*4+1;char*buf=(char*)malloc(cap);size_t n=0;const char*p=s;while(*p){uint32_t cp;int cn=sp_utf8_decode(p,&cp);size_t mi=fn;for(size_t j=0;j<fn;j++)if(fcps[j]==cp){mi=j;break;}if(mi<fn&&tn>0){uint32_t rep=mi<tn?tcps[mi]:tcps[tn-1];n+=sp_utf8_encode(rep,buf+n);}else{memcpy(buf+n,p,cn);n+=cn;}p+=cn;}buf[n]=0;char*r=sp_str_alloc(n);memcpy(r,buf,n+1);free(buf);free(fcps);free(tcps);return r;}
|
|
555
|
+
static const char*sp_str_delete(const char*s,const char*chars){size_t setn;uint32_t*set=sp_utf8_decode_all(chars,&setn);size_t bl=strlen(s);char*r=sp_str_alloc_raw(bl+1);size_t n=0;const char*p=s;while(*p){uint32_t cp;int cn=sp_utf8_decode(p,&cp);if(!sp_utf8_set_has(set,setn,cp)){memcpy(r+n,p,cn);n+=cn;}p+=cn;}r[n]=0;free(set);return r;}
|
|
556
|
+
static const char*sp_str_squeeze(const char*s){size_t bl=strlen(s);char*r=sp_str_alloc_raw(bl+1);size_t n=0;uint32_t prev=0xFFFFFFFFu;const char*p=s;while(*p){uint32_t cp;int cn=sp_utf8_decode(p,&cp);if(cp!=prev){memcpy(r+n,p,cn);n+=cn;prev=cp;}p+=cn;}r[n]=0;return r;}
|
|
557
|
+
static const char*sp_str_ljust(const char*s,mrb_int w){mrb_int cl=sp_str_length(s);if(cl>=w)return s;size_t bl=strlen(s);size_t pad=(size_t)(w-cl);char*r=sp_str_alloc_raw(bl+pad+1);memcpy(r,s,bl);memset(r+bl,' ',pad);r[bl+pad]=0;return r;}
|
|
558
|
+
static const char*sp_str_rjust(const char*s,mrb_int w){mrb_int cl=sp_str_length(s);if(cl>=w)return s;size_t bl=strlen(s);size_t pad=(size_t)(w-cl);char*r=sp_str_alloc_raw(bl+pad+1);memset(r,' ',pad);memcpy(r+pad,s,bl);r[bl+pad]=0;return r;}
|
|
559
|
+
static const char*sp_str_center(const char*s,mrb_int w){mrb_int cl=sp_str_length(s);if(cl>=w)return s;size_t bl=strlen(s);mrb_int pad=w-cl;mrb_int left=pad/2;mrb_int right=pad-left;char*r=sp_str_alloc_raw(bl+pad+1);memset(r,' ',left);memcpy(r+left,s,bl);memset(r+left+bl,' ',right);r[bl+pad]=0;return r;}
|
|
560
|
+
static const char*sp_str_ljust2(const char*s,mrb_int w,const char*pad){mrb_int cl=sp_str_length(s);if(cl>=w)return s;size_t bl=strlen(s);size_t pn;uint32_t*pcps=sp_utf8_decode_all(pad,&pn);if(pn==0){free(pcps);char*r=sp_str_alloc_raw(bl+1);memcpy(r,s,bl+1);return r;}mrb_int need=w-cl;size_t padb=0;for(mrb_int i=0;i<need;i++){char tmp[4];padb+=sp_utf8_encode(pcps[i%pn],tmp);}char*r=sp_str_alloc_raw(bl+padb+1);memcpy(r,s,bl);size_t n=bl;for(mrb_int i=0;i<need;i++)n+=sp_utf8_encode(pcps[i%pn],r+n);r[n]=0;free(pcps);return r;}
|
|
561
|
+
static const char*sp_str_rjust2(const char*s,mrb_int w,const char*pad){mrb_int cl=sp_str_length(s);if(cl>=w)return s;size_t bl=strlen(s);size_t pn;uint32_t*pcps=sp_utf8_decode_all(pad,&pn);if(pn==0){free(pcps);char*r=sp_str_alloc_raw(bl+1);memcpy(r,s,bl+1);return r;}mrb_int need=w-cl;size_t padb=0;for(mrb_int i=0;i<need;i++){char tmp[4];padb+=sp_utf8_encode(pcps[i%pn],tmp);}char*r=sp_str_alloc_raw(bl+padb+1);size_t n=0;for(mrb_int i=0;i<need;i++)n+=sp_utf8_encode(pcps[i%pn],r+n);memcpy(r+n,s,bl);r[n+bl]=0;free(pcps);return r;}
|
|
562
|
+
static const char*sp_str_center2(const char*s,mrb_int w,const char*pad){mrb_int cl=sp_str_length(s);if(cl>=w)return s;size_t bl=strlen(s);size_t pn;uint32_t*pcps=sp_utf8_decode_all(pad,&pn);if(pn==0){free(pcps);char*r=sp_str_alloc_raw(bl+1);memcpy(r,s,bl+1);return r;}mrb_int pd=w-cl;mrb_int left=pd/2;mrb_int right=pd-left;size_t leftb=0,rightb=0;{char tmp[4];for(mrb_int i=0;i<left;i++)leftb+=sp_utf8_encode(pcps[i%pn],tmp);for(mrb_int i=0;i<right;i++)rightb+=sp_utf8_encode(pcps[i%pn],tmp);}char*r=sp_str_alloc_raw(leftb+bl+rightb+1);size_t n=0;for(mrb_int i=0;i<left;i++)n+=sp_utf8_encode(pcps[i%pn],r+n);memcpy(r+n,s,bl);n+=bl;for(mrb_int i=0;i<right;i++)n+=sp_utf8_encode(pcps[i%pn],r+n);r[n]=0;free(pcps);return r;}
|
|
563
|
+
static const char*sp_str_lstrip(const char*s){while(*s&&isspace((unsigned char)*s))s++;char*r=sp_str_alloc_raw(strlen(s)+1);strcpy(r,s);return r;}
|
|
564
|
+
static const char*sp_str_rstrip(const char*s){size_t l=strlen(s);while(l>0&&isspace((unsigned char)s[l-1]))l--;char*r=sp_str_alloc_raw(l+1);memcpy(r,s,l);r[l]=0;return r;}
|
|
565
|
+
static const char*sp_str_dup(const char*s){char*r=sp_str_alloc_raw(strlen(s)+1);strcpy(r,s);return r;}
|
|
566
|
+
|
|
567
|
+
typedef struct{char*data;int64_t len;int64_t cap;}sp_String;
|
|
568
|
+
static void sp_String_fin(void*p){free(((sp_String*)p)->data);}
|
|
569
|
+
static sp_String*sp_String_new(const char*s){
|
|
570
|
+
/* Copy s's payload into a raw-malloc'd buffer BEFORE sp_gc_alloc.
|
|
571
|
+
If s is a heap string (sp_str_alloc) whose only liveness anchor
|
|
572
|
+
is this C stack frame, sp_gc_alloc can trigger sp_gc_collect →
|
|
573
|
+
sp_str_sweep, which would free s mid-call; a post-alloc strlen +
|
|
574
|
+
memcpy on s would then read freed memory (heap-use-after-free
|
|
575
|
+
under ASAN, non-deterministic crash on Windows MinGW where the
|
|
576
|
+
allocator reuses freed regions faster). The malloc'd buffer is
|
|
577
|
+
not on the GC heap and not on the string heap, so it survives
|
|
578
|
+
any GC inside sp_gc_alloc. */
|
|
579
|
+
int64_t len=(int64_t)strlen(s);
|
|
580
|
+
int64_t cap=len*2+16;
|
|
581
|
+
char*data=(char*)malloc(cap+1);
|
|
582
|
+
memcpy(data,s,len+1);
|
|
583
|
+
sp_String*r=(sp_String*)sp_gc_alloc(sizeof(sp_String),sp_String_fin,NULL);
|
|
584
|
+
r->len=len;r->cap=cap;r->data=data;
|
|
585
|
+
{sp_gc_hdr*h=(sp_gc_hdr*)((char*)r-sizeof(sp_gc_hdr));h->size+=r->cap+1;sp_gc_bytes+=r->cap+1;}
|
|
586
|
+
return r;
|
|
587
|
+
}
|
|
588
|
+
static inline void sp_String_append(sp_String*s,const char*t){int64_t tl=(int64_t)strlen(t);if(s->len+tl>=s->cap){sp_gc_hdr*h=(sp_gc_hdr*)((char*)s-sizeof(sp_gc_hdr));sp_gc_bytes-=s->cap+1;h->size-=s->cap+1;s->cap=(s->len+tl)*2+16;s->data=(char*)realloc(s->data,s->cap+1);h->size+=s->cap+1;sp_gc_bytes+=s->cap+1;}memcpy(s->data+s->len,t,tl+1);s->len+=tl;}
|
|
589
|
+
static inline void sp_String_prepend(sp_String*s,const char*t){int64_t tl=(int64_t)strlen(t);if(s->len+tl>=s->cap){sp_gc_hdr*h=(sp_gc_hdr*)((char*)s-sizeof(sp_gc_hdr));sp_gc_bytes-=s->cap+1;h->size-=s->cap+1;s->cap=(s->len+tl)*2+16;s->data=(char*)realloc(s->data,s->cap+1);h->size+=s->cap+1;sp_gc_bytes+=s->cap+1;}memmove(s->data+tl,s->data,s->len+1);memcpy(s->data,t,tl);s->len+=tl;}
|
|
590
|
+
static inline const char*sp_String_cstr(sp_String*s){return s->data;}
|
|
591
|
+
static inline int64_t sp_String_length(sp_String*s){return s->len;}
|
|
592
|
+
static sp_String*sp_String_dup(sp_String*s){return sp_String_new(s->data);}
|
|
593
|
+
/* Array#inspect for each typed array: `[elem1, elem2, ...]` with each
|
|
594
|
+
element rendered via its own primitive inspect. Matches CRuby's
|
|
595
|
+
Array#inspect output byte-for-byte. Returns a GC-managed C string. */
|
|
596
|
+
static const char*sp_IntArray_inspect(sp_IntArray*a){sp_String*s=sp_String_new("[");for(mrb_int i=0;i<a->len;i++){if(i>0)sp_String_append(s,", ");sp_String_append(s,sp_int_to_s(a->data[a->start+i]));}sp_String_append(s,"]");return s->data;}
|
|
597
|
+
static const char*sp_FloatArray_inspect(sp_FloatArray*a){sp_String*s=sp_String_new("[");for(mrb_int i=0;i<a->len;i++){if(i>0)sp_String_append(s,", ");sp_String_append(s,sp_float_inspect(a->data[i]));}sp_String_append(s,"]");return s->data;}
|
|
598
|
+
static const char*sp_StrArray_inspect(sp_StrArray*a){sp_String*s=sp_String_new("[");for(mrb_int i=0;i<a->len;i++){if(i>0)sp_String_append(s,", ");sp_String_append(s,sp_str_inspect(a->data[i]));}sp_String_append(s,"]");return s->data;}
|
|
599
|
+
/* Symbol arrays share the IntArray representation (sp_sym = mrb_int),
|
|
600
|
+
but each element is rendered as ":name" via sp_sym_to_s. */
|
|
601
|
+
static const char*sp_SymArray_inspect(sp_IntArray*a){sp_String*s=sp_String_new("[");for(mrb_int i=0;i<a->len;i++){if(i>0)sp_String_append(s,", ");sp_String_append(s,":");sp_String_append(s,sp_sym_to_s((sp_sym)a->data[a->start+i]));}sp_String_append(s,"]");return s->data;}
|
|
602
|
+
/* PtrArray elements are object pointers without a per-element class
|
|
603
|
+
tag, so we render them as `#<Object>` rather than recursing. */
|
|
604
|
+
static const char*sp_PtrArray_inspect(sp_PtrArray*a){sp_String*s=sp_String_new("[");for(mrb_int i=0;i<a->len;i++){if(i>0)sp_String_append(s,", ");sp_String_append(s,"#<Object>");}sp_String_append(s,"]");return s->data;}
|
|
605
|
+
/* Nested-array inspect: when codegen knows the ptr_array's element
|
|
606
|
+
type is one of the four built-in T_array shapes, recurse into the
|
|
607
|
+
matching primitive inspect (issue #169). */
|
|
608
|
+
static const char*sp_IntArrayPtrArray_inspect(sp_PtrArray*a){sp_String*s=sp_String_new("[");for(mrb_int i=0;i<a->len;i++){if(i>0)sp_String_append(s,", ");sp_String_append(s,sp_IntArray_inspect((sp_IntArray*)a->data[i]));}sp_String_append(s,"]");return s->data;}
|
|
609
|
+
static const char*sp_FloatArrayPtrArray_inspect(sp_PtrArray*a){sp_String*s=sp_String_new("[");for(mrb_int i=0;i<a->len;i++){if(i>0)sp_String_append(s,", ");sp_String_append(s,sp_FloatArray_inspect((sp_FloatArray*)a->data[i]));}sp_String_append(s,"]");return s->data;}
|
|
610
|
+
static const char*sp_StrArrayPtrArray_inspect(sp_PtrArray*a){sp_String*s=sp_String_new("[");for(mrb_int i=0;i<a->len;i++){if(i>0)sp_String_append(s,", ");sp_String_append(s,sp_StrArray_inspect((sp_StrArray*)a->data[i]));}sp_String_append(s,"]");return s->data;}
|
|
611
|
+
static const char*sp_SymArrayPtrArray_inspect(sp_PtrArray*a){sp_String*s=sp_String_new("[");for(mrb_int i=0;i<a->len;i++){if(i>0)sp_String_append(s,", ");sp_String_append(s,sp_SymArray_inspect((sp_IntArray*)a->data[i]));}sp_String_append(s,"]");return s->data;}
|
|
612
|
+
|
|
613
|
+
/* Regexp engine (link with libspre.a from lib/regexp/) */
|
|
614
|
+
typedef struct mrb_regexp_pattern mrb_regexp_pattern;
|
|
615
|
+
mrb_regexp_pattern* re_compile(const char *pattern, int64_t len, uint32_t flags);
|
|
616
|
+
void re_free(mrb_regexp_pattern *pat);
|
|
617
|
+
int re_exec(const mrb_regexp_pattern *pat, const char *str, int64_t len, int64_t start, int *captures, int captures_size);
|
|
618
|
+
|
|
619
|
+
/* Regexp globals: $1-$9 captures */
|
|
620
|
+
static const char *sp_re_captures[10] = {0};
|
|
621
|
+
static int sp_re_caps[64];
|
|
622
|
+
/* NULL (not "") so sp_mark_string's null-guard handles the unset case
|
|
623
|
+
without reaching the `s[-1]` access. The rodata `""` literal would
|
|
624
|
+
trigger -Wstringop-overflow under -O3 + sp_mark_string inlining at
|
|
625
|
+
the call site in sp_re_mark_globals — gcc proves the `s[-1] = 0xfc`
|
|
626
|
+
write would be out-of-bounds even though the runtime guard
|
|
627
|
+
`s[-1] == 0xfe` (always false for rodata) prevents it from firing. */
|
|
628
|
+
static const char *sp_re_last_str = NULL;
|
|
629
|
+
|
|
630
|
+
/* Symbolic back-references populated alongside the numbered captures.
|
|
631
|
+
Read by codegen's BackReferenceReadNode arm:
|
|
632
|
+
$& -> sp_re_match_str (the whole matched substring)
|
|
633
|
+
$` -> sp_re_match_pre (substring before the match)
|
|
634
|
+
$' -> sp_re_match_post (substring after the match)
|
|
635
|
+
$~ falls back to $& since Spinel has no MatchData wrapper. */
|
|
636
|
+
static const char *sp_re_match_str = NULL;
|
|
637
|
+
static const char *sp_re_match_pre = NULL;
|
|
638
|
+
static const char *sp_re_match_post = NULL;
|
|
639
|
+
|
|
640
|
+
/* Mark the regex globals as live during GC. Each holds a pointer to a
|
|
641
|
+
string allocated via sp_str_alloc_raw on the str-heap; without this
|
|
642
|
+
sp_str_sweep would reap them on the next collect, leaving dangling
|
|
643
|
+
pointers in $1..$9, $&, $`, $'. sp_mark_string is null-safe and
|
|
644
|
+
no-ops on non-heap strings (the empty-string default of
|
|
645
|
+
sp_re_last_str), so it's safe to call unconditionally. */
|
|
646
|
+
static void sp_re_mark_globals(void) {
|
|
647
|
+
sp_mark_string(sp_re_last_str);
|
|
648
|
+
for (int i = 0; i < 10; i++) sp_mark_string(sp_re_captures[i]);
|
|
649
|
+
sp_mark_string(sp_re_match_str);
|
|
650
|
+
sp_mark_string(sp_re_match_pre);
|
|
651
|
+
sp_mark_string(sp_re_match_post);
|
|
652
|
+
}
|
|
653
|
+
|
|
654
|
+
static void sp_re_set_captures(const char *str, int *caps, int ncaps) {
|
|
655
|
+
sp_re_last_str = str;
|
|
656
|
+
for (int i = 0; i < 10; i++) sp_re_captures[i] = NULL;
|
|
657
|
+
for (int i = 1; i < ncaps && i < 10; i++) {
|
|
658
|
+
if (caps[i*2] >= 0 && caps[i*2+1] >= 0) {
|
|
659
|
+
int len = caps[i*2+1] - caps[i*2];
|
|
660
|
+
char *buf = sp_str_alloc_raw(len+1);
|
|
661
|
+
memcpy(buf, str+caps[i*2], len); buf[len] = 0;
|
|
662
|
+
sp_re_captures[i] = buf;
|
|
663
|
+
}
|
|
664
|
+
}
|
|
665
|
+
/* Populate the symbolic back-references from caps[0]/[1] (the whole
|
|
666
|
+
match span). NULL when the match failed; the codegen ternary
|
|
667
|
+
falls back to "". */
|
|
668
|
+
sp_re_match_str = NULL;
|
|
669
|
+
sp_re_match_pre = NULL;
|
|
670
|
+
sp_re_match_post = NULL;
|
|
671
|
+
if (ncaps >= 1 && caps[0] >= 0 && caps[1] >= 0) {
|
|
672
|
+
int slen = (int)strlen(str);
|
|
673
|
+
int mlen = caps[1] - caps[0];
|
|
674
|
+
char *m = sp_str_alloc_raw(mlen + 1);
|
|
675
|
+
memcpy(m, str + caps[0], mlen); m[mlen] = 0;
|
|
676
|
+
sp_re_match_str = m;
|
|
677
|
+
char *pre = sp_str_alloc_raw(caps[0] + 1);
|
|
678
|
+
memcpy(pre, str, caps[0]); pre[caps[0]] = 0;
|
|
679
|
+
sp_re_match_pre = pre;
|
|
680
|
+
int post_len = slen - caps[1];
|
|
681
|
+
char *post = sp_str_alloc_raw(post_len + 1);
|
|
682
|
+
memcpy(post, str + caps[1], post_len); post[post_len] = 0;
|
|
683
|
+
sp_re_match_post = post;
|
|
684
|
+
}
|
|
685
|
+
}
|
|
686
|
+
|
|
687
|
+
static mrb_int sp_re_match(mrb_regexp_pattern *pat, const char *str) {
|
|
688
|
+
int64_t slen = (int64_t)strlen(str);
|
|
689
|
+
int ncaps = 32;
|
|
690
|
+
int n = re_exec(pat, str, slen, 0, sp_re_caps, ncaps);
|
|
691
|
+
if (n > 0) { sp_re_set_captures(str, sp_re_caps, n/2); return sp_re_caps[0] + 1; }
|
|
692
|
+
return 0;
|
|
693
|
+
}
|
|
694
|
+
|
|
695
|
+
static mrb_bool sp_re_match_p(mrb_regexp_pattern *pat, const char *str) {
|
|
696
|
+
int64_t slen = (int64_t)strlen(str);
|
|
697
|
+
int caps[2];
|
|
698
|
+
return re_exec(pat, str, slen, 0, caps, 2) > 0;
|
|
699
|
+
}
|
|
700
|
+
|
|
701
|
+
static const char *sp_re_gsub(mrb_regexp_pattern *pat, const char *str, const char *rep) {
|
|
702
|
+
int64_t slen = (int64_t)strlen(str); size_t rlen = strlen(rep);
|
|
703
|
+
size_t cap = slen * 2 + 64; char *out = sp_str_alloc_raw(cap); size_t olen = 0;
|
|
704
|
+
int64_t pos = 0; int caps[64];
|
|
705
|
+
while (pos <= slen) {
|
|
706
|
+
int n = re_exec(pat, str, slen, pos, caps, 64);
|
|
707
|
+
if (n <= 0 || caps[0] < 0) break;
|
|
708
|
+
size_t before = caps[0] - pos;
|
|
709
|
+
if (olen+before+rlen >= cap) { cap = (olen+before+rlen)*2+64; out = (char*)realloc(out, cap); }
|
|
710
|
+
memcpy(out+olen, str+pos, before); olen += before;
|
|
711
|
+
memcpy(out+olen, rep, rlen); olen += rlen;
|
|
712
|
+
pos = caps[1]; if (caps[0] == caps[1]) pos++;
|
|
713
|
+
}
|
|
714
|
+
size_t rest = slen - pos;
|
|
715
|
+
if (olen+rest >= cap) { cap = olen+rest+1; out = (char*)realloc(out, cap); }
|
|
716
|
+
memcpy(out+olen, str+pos, rest); olen += rest;
|
|
717
|
+
out[olen] = 0; return out;
|
|
718
|
+
}
|
|
719
|
+
|
|
720
|
+
static const char *sp_re_sub(mrb_regexp_pattern *pat, const char *str, const char *rep) {
|
|
721
|
+
int64_t slen = (int64_t)strlen(str); size_t rlen = strlen(rep);
|
|
722
|
+
int caps[64];
|
|
723
|
+
int n = re_exec(pat, str, slen, 0, caps, 64);
|
|
724
|
+
if (n <= 0 || caps[0] < 0) return str;
|
|
725
|
+
size_t out_len = caps[0] + rlen + (slen - caps[1]);
|
|
726
|
+
char *out = sp_str_alloc_raw(out_len + 1);
|
|
727
|
+
memcpy(out, str, caps[0]);
|
|
728
|
+
memcpy(out+caps[0], rep, rlen);
|
|
729
|
+
memcpy(out+caps[0]+rlen, str+caps[1], slen-caps[1]+1);
|
|
730
|
+
return out;
|
|
731
|
+
}
|
|
732
|
+
|
|
733
|
+
static sp_StrArray *sp_re_scan(mrb_regexp_pattern *pat, const char *str) {
|
|
734
|
+
sp_StrArray *arr = sp_StrArray_new();
|
|
735
|
+
int64_t slen = (int64_t)strlen(str); int64_t pos = 0; int caps[64];
|
|
736
|
+
while (pos <= slen) {
|
|
737
|
+
int n = re_exec(pat, str, slen, pos, caps, 64);
|
|
738
|
+
if (n <= 0 || caps[0] < 0) break;
|
|
739
|
+
int len = caps[1] - caps[0];
|
|
740
|
+
char *m = sp_str_alloc_raw(len+1); memcpy(m, str+caps[0], len); m[len] = 0;
|
|
741
|
+
sp_StrArray_push(arr, m);
|
|
742
|
+
pos = caps[1]; if (caps[0] == caps[1]) pos++;
|
|
743
|
+
}
|
|
744
|
+
return arr;
|
|
745
|
+
}
|
|
746
|
+
|
|
747
|
+
static sp_StrArray *sp_re_split(mrb_regexp_pattern *pat, const char *str) {
|
|
748
|
+
sp_StrArray *arr = sp_StrArray_new();
|
|
749
|
+
int64_t slen = (int64_t)strlen(str); int64_t pos = 0; int caps[64];
|
|
750
|
+
while (pos <= slen) {
|
|
751
|
+
int n = re_exec(pat, str, slen, pos, caps, 64);
|
|
752
|
+
if (n <= 0 || caps[0] < 0) {
|
|
753
|
+
int len = slen - pos; char *m = sp_str_alloc_raw(len+1);
|
|
754
|
+
memcpy(m, str+pos, len); m[len] = 0; sp_StrArray_push(arr, m); break;
|
|
755
|
+
}
|
|
756
|
+
int len = caps[0] - pos; char *m = sp_str_alloc_raw(len+1);
|
|
757
|
+
memcpy(m, str+pos, len); m[len] = 0; sp_StrArray_push(arr, m);
|
|
758
|
+
pos = caps[1]; if (caps[0] == caps[1]) pos++;
|
|
759
|
+
}
|
|
760
|
+
return arr;
|
|
761
|
+
}
|
|
762
|
+
|
|
763
|
+
/* NaN-boxed polymorphic value */
|
|
764
|
+
typedef uint64_t sp_RbValue;
|
|
765
|
+
#define SP_TAG_INT 0
|
|
766
|
+
#define SP_TAG_STR 1
|
|
767
|
+
#define SP_TAG_FLT 2
|
|
768
|
+
#define SP_TAG_BOOL 3
|
|
769
|
+
#define SP_TAG_NIL 4
|
|
770
|
+
#define SP_TAG_OBJ 5
|
|
771
|
+
#define SP_TAG_SYM 6
|
|
772
|
+
/* Negative cls_id values let SP_TAG_OBJ also carry built-in pointer
|
|
773
|
+
types (IntArray, FloatArray, ...) — avoids minting a new SP_TAG_*
|
|
774
|
+
per type. Non-negative cls_id stays an index into the user-class
|
|
775
|
+
table as before. The element-type tag and the array cls_id are
|
|
776
|
+
paired by `array_cls_id = -element_tag - 1`. */
|
|
777
|
+
#define SP_BUILTIN_ARRAY_OF(tag) (-(tag) - 1)
|
|
778
|
+
#define SP_BUILTIN_INT_ARRAY SP_BUILTIN_ARRAY_OF(SP_TAG_INT) /* -1 */
|
|
779
|
+
#define SP_BUILTIN_STR_ARRAY SP_BUILTIN_ARRAY_OF(SP_TAG_STR) /* -2 */
|
|
780
|
+
#define SP_BUILTIN_FLT_ARRAY SP_BUILTIN_ARRAY_OF(SP_TAG_FLT) /* -3 */
|
|
781
|
+
#define SP_BUILTIN_PTR_ARRAY SP_BUILTIN_ARRAY_OF(SP_TAG_OBJ) /* -6 */
|
|
782
|
+
#define SP_BUILTIN_SYM_ARRAY SP_BUILTIN_ARRAY_OF(SP_TAG_SYM) /* -7 */
|
|
783
|
+
#define SP_BUILTIN_PROC (-9) /* sp_Proc *, distinct from any tag-based id */
|
|
784
|
+
#define SP_BUILTIN_RANGE (-10) /* sp_Range *, heap copy of stack-typed sp_Range when crossing into poly */
|
|
785
|
+
#define SP_BUILTIN_TIME (-11) /* sp_Time *, heap copy of stack-typed sp_Time when crossing into poly */
|
|
786
|
+
#define SP_BUILTIN_POLY_ARRAY (-12) /* sp_PolyArray *, array of sp_RbVal */
|
|
787
|
+
/* Hash variant cls_ids — boxed into the cls_id of a poly slot so
|
|
788
|
+
Hash#dig can recover the concrete hash type at runtime. */
|
|
789
|
+
#define SP_BUILTIN_STR_INT_HASH (-13)
|
|
790
|
+
#define SP_BUILTIN_STR_STR_HASH (-14)
|
|
791
|
+
#define SP_BUILTIN_INT_STR_HASH (-15)
|
|
792
|
+
#define SP_BUILTIN_SYM_INT_HASH (-16)
|
|
793
|
+
#define SP_BUILTIN_SYM_STR_HASH (-17)
|
|
794
|
+
#define SP_BUILTIN_STR_POLY_HASH (-18)
|
|
795
|
+
#define SP_BUILTIN_SYM_POLY_HASH (-19)
|
|
796
|
+
#define SP_BUILTIN_POLY_POLY_HASH (-20)
|
|
797
|
+
typedef struct { int tag; int cls_id; union { mrb_int i; const char *s; mrb_float f; mrb_bool b; void *p; } v; } sp_RbVal;
|
|
798
|
+
static sp_RbVal sp_box_int(mrb_int v) { sp_RbVal r; r.tag = SP_TAG_INT; r.cls_id = 0; r.v.i = v; return r; }
|
|
799
|
+
static sp_RbVal sp_box_str(const char *v) { sp_RbVal r; r.tag = SP_TAG_STR; r.cls_id = 0; r.v.s = v; return r; }
|
|
800
|
+
static sp_RbVal sp_box_float(mrb_float v) { sp_RbVal r; r.tag = SP_TAG_FLT; r.cls_id = 0; r.v.f = v; return r; }
|
|
801
|
+
static sp_RbVal sp_box_bool(mrb_bool v) { sp_RbVal r; r.tag = SP_TAG_BOOL; r.cls_id = 0; r.v.b = v; return r; }
|
|
802
|
+
static sp_RbVal sp_box_nil(void) { sp_RbVal r; r.tag = SP_TAG_NIL; r.cls_id = 0; r.v.i = 0; return r; }
|
|
803
|
+
static sp_RbVal sp_box_obj(void *p, int cls_id) { sp_RbVal r; r.tag = SP_TAG_OBJ; r.cls_id = cls_id; r.v.p = p; return r; }
|
|
804
|
+
static sp_RbVal sp_box_sym(sp_sym v) { sp_RbVal r; r.tag = SP_TAG_SYM; r.cls_id = 0; r.v.i = (mrb_int)v; return r; }
|
|
805
|
+
static sp_RbVal sp_box_nullable_str(const char *v) { return v ? sp_box_str(v) : sp_box_nil(); }
|
|
806
|
+
static sp_RbVal sp_box_nullable_obj(void *p, int cls_id) { return p ? sp_box_obj(p, cls_id) : sp_box_nil(); }
|
|
807
|
+
/* Built-in pointer boxes — share SP_TAG_OBJ with a reserved negative
|
|
808
|
+
cls_id so the dispatch path is uniform. */
|
|
809
|
+
static sp_RbVal sp_box_int_array(void *p) { return sp_box_obj(p, SP_BUILTIN_INT_ARRAY); }
|
|
810
|
+
static sp_RbVal sp_box_float_array(void *p) { return sp_box_obj(p, SP_BUILTIN_FLT_ARRAY); }
|
|
811
|
+
static sp_RbVal sp_box_str_array(void *p) { return sp_box_obj(p, SP_BUILTIN_STR_ARRAY); }
|
|
812
|
+
static sp_RbVal sp_box_sym_array(void *p) { return sp_box_obj(p, SP_BUILTIN_SYM_ARRAY); }
|
|
813
|
+
static sp_RbVal sp_box_ptr_array(void *p) { return sp_box_obj(p, SP_BUILTIN_PTR_ARRAY); }
|
|
814
|
+
static sp_RbVal sp_box_proc(void *p) { return sp_box_obj(p, SP_BUILTIN_PROC); }
|
|
815
|
+
/* sp_Range is a 16-byte value type that doesn't fit in sp_RbVal's union
|
|
816
|
+
(max 8 bytes). When a Range crosses into a poly slot (heterogeneous
|
|
817
|
+
hash / array / param / ivar), copy it onto the GC heap and box the
|
|
818
|
+
pointer via SP_BUILTIN_RANGE. The Range has no internal pointer fields
|
|
819
|
+
so no scanner is needed. */
|
|
820
|
+
static sp_RbVal sp_box_range(sp_Range v) {
|
|
821
|
+
sp_Range *p = (sp_Range *)sp_gc_alloc(sizeof(sp_Range), NULL, NULL);
|
|
822
|
+
*p = v;
|
|
823
|
+
return sp_box_obj(p, SP_BUILTIN_RANGE);
|
|
824
|
+
}
|
|
825
|
+
static const char *sp_Range_inspect(sp_Range *r) {
|
|
826
|
+
/* "first..last" form. Buffer sized for two int64s plus the dots. */
|
|
827
|
+
char *buf = sp_str_alloc_raw(48);
|
|
828
|
+
snprintf(buf, 48, "%lld..%lld", (long long)r->first, (long long)r->last);
|
|
829
|
+
return buf;
|
|
830
|
+
}
|
|
831
|
+
/* Same heap-box rationale as sp_Range: sp_Time is 12+ bytes (tv_sec +
|
|
832
|
+
tv_nsec), wider than sp_RbVal's 8-byte union. No internal pointers
|
|
833
|
+
so no scanner is needed. */
|
|
834
|
+
static sp_RbVal sp_box_time(sp_Time v) {
|
|
835
|
+
sp_Time *p = (sp_Time *)sp_gc_alloc(sizeof(sp_Time), NULL, NULL);
|
|
836
|
+
*p = v;
|
|
837
|
+
return sp_box_obj(p, SP_BUILTIN_TIME);
|
|
838
|
+
}
|
|
839
|
+
static const char *sp_Time_inspect(sp_Time *t) {
|
|
840
|
+
/* "YYYY-MM-DD HH:MM:SS UTC" form via gmtime. CRuby uses localtime
|
|
841
|
+
with a numeric tz offset, but the spinel runtime keeps Time
|
|
842
|
+
timezone-naive — UTC is the unambiguous choice that doesn't need
|
|
843
|
+
the platform's tzdata. Buffer is 32 chars + a margin. */
|
|
844
|
+
char *buf = sp_str_alloc_raw(40);
|
|
845
|
+
time_t sec = (time_t)t->tv_sec;
|
|
846
|
+
struct tm *tm_ = gmtime(&sec);
|
|
847
|
+
if (tm_) {
|
|
848
|
+
strftime(buf, 40, "%Y-%m-%d %H:%M:%S UTC", tm_);
|
|
849
|
+
} else {
|
|
850
|
+
snprintf(buf, 40, "Time(%lld)", (long long)t->tv_sec);
|
|
851
|
+
}
|
|
852
|
+
return buf;
|
|
853
|
+
}
|
|
854
|
+
static sp_RbVal sp_box_poly_array(void *p) { return sp_box_obj(p, SP_BUILTIN_POLY_ARRAY); }
|
|
855
|
+
static void sp_poly_puts(sp_RbVal v) {
|
|
856
|
+
switch (v.tag) {
|
|
857
|
+
case SP_TAG_INT: printf("%lld\n", (long long)v.v.i); break;
|
|
858
|
+
case SP_TAG_STR: if (v.v.s) { fputs(v.v.s, stdout); if (!*v.v.s || v.v.s[strlen(v.v.s)-1] != '\n') putchar('\n'); } else putchar('\n'); break;
|
|
859
|
+
case SP_TAG_FLT: { fputs(sp_float_to_s(v.v.f), stdout); putchar('\n'); break; }
|
|
860
|
+
case SP_TAG_BOOL: puts(v.v.b ? "true" : "false"); break;
|
|
861
|
+
case SP_TAG_NIL: putchar('\n'); break;
|
|
862
|
+
case SP_TAG_SYM: { const char *_ss = sp_sym_to_s((sp_sym)v.v.i); fputs(_ss, stdout); putchar('\n'); break; }
|
|
863
|
+
case SP_TAG_OBJ: {
|
|
864
|
+
/* Built-in pointer types route through their inspect helpers
|
|
865
|
+
(one line per array, matching `puts arr`'s short form). User
|
|
866
|
+
classes (cls_id >= 0) fall through to the raw-pointer path
|
|
867
|
+
since we don't have per-class inspect dispatch here. */
|
|
868
|
+
switch (v.cls_id) {
|
|
869
|
+
case SP_BUILTIN_INT_ARRAY: puts(sp_IntArray_inspect((sp_IntArray *)v.v.p)); break;
|
|
870
|
+
case SP_BUILTIN_FLT_ARRAY: puts(sp_FloatArray_inspect((sp_FloatArray *)v.v.p)); break;
|
|
871
|
+
case SP_BUILTIN_STR_ARRAY: puts(sp_StrArray_inspect((sp_StrArray *)v.v.p)); break;
|
|
872
|
+
case SP_BUILTIN_SYM_ARRAY: puts(sp_SymArray_inspect((sp_IntArray *)v.v.p)); break;
|
|
873
|
+
case SP_BUILTIN_PTR_ARRAY: puts(sp_PtrArray_inspect((sp_PtrArray *)v.v.p)); break;
|
|
874
|
+
case SP_BUILTIN_RANGE: puts(sp_Range_inspect((sp_Range *)v.v.p)); break;
|
|
875
|
+
case SP_BUILTIN_TIME: puts(sp_Time_inspect((sp_Time *)v.v.p)); break;
|
|
876
|
+
default: printf("#<Object:0x%p>\n", v.v.p); break;
|
|
877
|
+
}
|
|
878
|
+
break;
|
|
879
|
+
}
|
|
880
|
+
default: printf("%lld\n", (long long)v.v.i); break;
|
|
881
|
+
}
|
|
882
|
+
}
|
|
883
|
+
static mrb_bool sp_poly_nil_p(sp_RbVal v) { return v.tag == SP_TAG_NIL; }
|
|
884
|
+
static mrb_bool sp_poly_truthy(sp_RbVal v) { return !(v.tag == SP_TAG_NIL || (v.tag == SP_TAG_BOOL && !v.v.b)); }
|
|
885
|
+
static const char *sp_poly_to_s(sp_RbVal v) {
|
|
886
|
+
switch (v.tag) {
|
|
887
|
+
case SP_TAG_INT: return sp_int_to_s(v.v.i);
|
|
888
|
+
case SP_TAG_STR: return v.v.s ? v.v.s : sp_str_empty;
|
|
889
|
+
case SP_TAG_FLT: return sp_float_to_s(v.v.f);
|
|
890
|
+
case SP_TAG_BOOL: return v.v.b ? SPL("true") : SPL("false");
|
|
891
|
+
case SP_TAG_NIL: return sp_str_empty;
|
|
892
|
+
case SP_TAG_SYM: return sp_sym_to_s((sp_sym)v.v.i);
|
|
893
|
+
case SP_TAG_OBJ:
|
|
894
|
+
switch (v.cls_id) {
|
|
895
|
+
case SP_BUILTIN_INT_ARRAY: return sp_IntArray_inspect((sp_IntArray *)v.v.p);
|
|
896
|
+
case SP_BUILTIN_FLT_ARRAY: return sp_FloatArray_inspect((sp_FloatArray *)v.v.p);
|
|
897
|
+
case SP_BUILTIN_STR_ARRAY: return sp_StrArray_inspect((sp_StrArray *)v.v.p);
|
|
898
|
+
case SP_BUILTIN_SYM_ARRAY: return sp_SymArray_inspect((sp_IntArray *)v.v.p);
|
|
899
|
+
case SP_BUILTIN_PTR_ARRAY: return sp_PtrArray_inspect((sp_PtrArray *)v.v.p);
|
|
900
|
+
case SP_BUILTIN_RANGE: return sp_Range_inspect((sp_Range *)v.v.p);
|
|
901
|
+
case SP_BUILTIN_TIME: return sp_Time_inspect((sp_Time *)v.v.p);
|
|
902
|
+
default: return sp_str_empty;
|
|
903
|
+
}
|
|
904
|
+
default: return sp_str_empty;
|
|
905
|
+
}
|
|
906
|
+
}
|
|
907
|
+
static sp_RbVal sp_poly_add(sp_RbVal a, sp_RbVal b) { if (a.tag == SP_TAG_INT && b.tag == SP_TAG_INT) return sp_box_int(a.v.i + b.v.i); if (a.tag == SP_TAG_FLT && b.tag == SP_TAG_FLT) return sp_box_float(a.v.f + b.v.f); if (a.tag == SP_TAG_INT && b.tag == SP_TAG_FLT) return sp_box_float((mrb_float)a.v.i + b.v.f); if (a.tag == SP_TAG_FLT && b.tag == SP_TAG_INT) return sp_box_float(a.v.f + (mrb_float)b.v.i); if (a.tag == SP_TAG_STR && b.tag == SP_TAG_STR) return sp_box_str(sp_str_concat(a.v.s, b.v.s)); return sp_box_int(0); }
|
|
908
|
+
static sp_RbVal sp_poly_sub(sp_RbVal a, sp_RbVal b) { if (a.tag == SP_TAG_INT && b.tag == SP_TAG_INT) return sp_box_int(a.v.i - b.v.i); if (a.tag == SP_TAG_FLT && b.tag == SP_TAG_FLT) return sp_box_float(a.v.f - b.v.f); return sp_box_int(0); }
|
|
909
|
+
static sp_RbVal sp_poly_mul(sp_RbVal a, sp_RbVal b) { if (a.tag == SP_TAG_INT && b.tag == SP_TAG_INT) return sp_box_int(a.v.i * b.v.i); if (a.tag == SP_TAG_FLT && b.tag == SP_TAG_FLT) return sp_box_float(a.v.f * b.v.f); if (a.tag == SP_TAG_INT && b.tag == SP_TAG_FLT) return sp_box_float((mrb_float)a.v.i * b.v.f); if (a.tag == SP_TAG_FLT && b.tag == SP_TAG_INT) return sp_box_float(a.v.f * (mrb_float)b.v.i); return sp_box_int(0); }
|
|
910
|
+
static mrb_int sp_poly_to_i(sp_RbVal v) { if (v.tag == SP_TAG_INT || v.tag == SP_TAG_SYM) return v.v.i; if (v.tag == SP_TAG_STR) return (mrb_int)strtoll(v.v.s ? v.v.s : sp_str_empty, NULL, 10); if (v.tag == SP_TAG_FLT) return (mrb_int)v.v.f; if (v.tag == SP_TAG_BOOL) return v.v.b ? 1 : 0; return 0; }
|
|
911
|
+
static mrb_float sp_poly_to_f(sp_RbVal v) { if (v.tag == SP_TAG_FLT) return v.v.f; if (v.tag == SP_TAG_INT || v.tag == SP_TAG_SYM) return (mrb_float)v.v.i; if (v.tag == SP_TAG_BOOL) return v.v.b ? 1.0 : 0.0; return 0.0; }
|
|
912
|
+
static mrb_bool sp_poly_numeric_p(sp_RbVal v) { return v.tag == SP_TAG_INT || v.tag == SP_TAG_FLT; }
|
|
913
|
+
static mrb_bool sp_poly_eq(sp_RbVal a, sp_RbVal b) { if (sp_poly_numeric_p(a) && sp_poly_numeric_p(b)) return sp_poly_to_f(a) == sp_poly_to_f(b); if (a.tag != b.tag) return FALSE; switch (a.tag) { case SP_TAG_INT: return a.v.i == b.v.i; case SP_TAG_STR: return (a.v.s == NULL || b.v.s == NULL) ? (a.v.s == b.v.s) : (strcmp(a.v.s, b.v.s) == 0); case SP_TAG_FLT: return a.v.f == b.v.f; case SP_TAG_BOOL: return a.v.b == b.v.b; case SP_TAG_NIL: return TRUE; case SP_TAG_SYM: return a.v.i == b.v.i; case SP_TAG_OBJ: return a.cls_id == b.cls_id && a.v.p == b.v.p; default: return FALSE; } }
|
|
914
|
+
static mrb_int sp_poly_cmp(sp_RbVal a, sp_RbVal b, mrb_bool *comparable) { if (sp_poly_numeric_p(a) && sp_poly_numeric_p(b)) { mrb_float af = sp_poly_to_f(a), bf = sp_poly_to_f(b); *comparable = TRUE; return (af > bf) - (af < bf); } if (a.tag == SP_TAG_STR && b.tag == SP_TAG_STR) { if (a.v.s == NULL || b.v.s == NULL) { *comparable = (a.v.s == b.v.s); return 0; } *comparable = TRUE; return strcmp(a.v.s, b.v.s); } if (a.tag == SP_TAG_SYM && b.tag == SP_TAG_SYM) { *comparable = TRUE; return (a.v.i > b.v.i) - (a.v.i < b.v.i); } *comparable = FALSE; return 0; }
|
|
915
|
+
static mrb_bool sp_poly_lt(sp_RbVal a, sp_RbVal b) { mrb_bool comparable; mrb_int cmp = sp_poly_cmp(a, b, &comparable); return comparable ? (cmp < 0) : FALSE; }
|
|
916
|
+
static mrb_bool sp_poly_le(sp_RbVal a, sp_RbVal b) { mrb_bool comparable; mrb_int cmp = sp_poly_cmp(a, b, &comparable); return comparable ? (cmp <= 0) : FALSE; }
|
|
917
|
+
static mrb_bool sp_poly_gt(sp_RbVal a, sp_RbVal b) { mrb_bool comparable; mrb_int cmp = sp_poly_cmp(a, b, &comparable); return comparable ? (cmp > 0) : FALSE; }
|
|
918
|
+
static mrb_bool sp_poly_ge(sp_RbVal a, sp_RbVal b) { mrb_bool comparable; mrb_int cmp = sp_poly_cmp(a, b, &comparable); return comparable ? (cmp >= 0) : FALSE; }
|
|
919
|
+
static sp_RbVal sp_poly_div(sp_RbVal a, sp_RbVal b) { if (a.tag == SP_TAG_FLT || b.tag == SP_TAG_FLT) return sp_box_float(sp_poly_to_f(a) / sp_poly_to_f(b)); return sp_box_int(sp_idiv(sp_poly_to_i(a), sp_poly_to_i(b))); }
|
|
920
|
+
static sp_RbVal sp_poly_mod(sp_RbVal a, sp_RbVal b) { if (a.tag == SP_TAG_FLT || b.tag == SP_TAG_FLT) return sp_box_float(fmod(sp_poly_to_f(a), sp_poly_to_f(b))); return sp_box_int(sp_imod(sp_poly_to_i(a), sp_poly_to_i(b))); }
|
|
921
|
+
static sp_RbVal sp_poly_pow(sp_RbVal a, sp_RbVal b) { double r = pow((double)sp_poly_to_f(a), (double)sp_poly_to_f(b)); if (a.tag == SP_TAG_INT && b.tag == SP_TAG_INT && b.v.i >= 0) return sp_box_int((mrb_int)r); return sp_box_float((mrb_float)r); }
|
|
922
|
+
/* sp_poly_shl is defined after sp_PolyArray_push (below) so the
|
|
923
|
+
push-dispatch path can call it directly. The Integer-bit-shift
|
|
924
|
+
semantics fall through when the recv isn't an array. */
|
|
925
|
+
static sp_RbVal sp_poly_shl(sp_RbVal a, sp_RbVal b);
|
|
926
|
+
static sp_RbVal sp_poly_shr(sp_RbVal a, sp_RbVal b) { return sp_box_int(sp_poly_to_i(a) >> sp_poly_to_i(b)); }
|
|
927
|
+
static sp_RbVal sp_poly_band(sp_RbVal a, sp_RbVal b) { return sp_box_int(sp_poly_to_i(a) & sp_poly_to_i(b)); }
|
|
928
|
+
static sp_RbVal sp_poly_bor(sp_RbVal a, sp_RbVal b) { return sp_box_int(sp_poly_to_i(a) | sp_poly_to_i(b)); }
|
|
929
|
+
static sp_RbVal sp_poly_bxor(sp_RbVal a, sp_RbVal b) { return sp_box_int(sp_poly_to_i(a) ^ sp_poly_to_i(b)); }
|
|
930
|
+
static sp_RbVal sp_poly_neg(sp_RbVal a) { if (a.tag == SP_TAG_FLT) return sp_box_float(-a.v.f); return sp_box_int(-sp_poly_to_i(a)); }
|
|
931
|
+
|
|
932
|
+
/* PolyArray: array of sp_RbVal */
|
|
933
|
+
typedef struct { sp_RbVal *data; mrb_int len; mrb_int cap; } sp_PolyArray;
|
|
934
|
+
static inline void sp_mark_rbval(sp_RbVal v);
|
|
935
|
+
static void sp_PolyArray_scan(void *p) { sp_PolyArray *a = (sp_PolyArray *)p; for (mrb_int i = 0; i < a->len; i++) sp_mark_rbval(a->data[i]); }
|
|
936
|
+
static void sp_PolyArray_fin(void *p) { sp_PolyArray *a = (sp_PolyArray *)p; sp_gc_hdr *h = (sp_gc_hdr *)((char *)a - sizeof(sp_gc_hdr)); sp_gc_bytes -= sizeof(sp_RbVal) * a->cap; h->size -= sizeof(sp_RbVal) * a->cap; free(a->data); }
|
|
937
|
+
static sp_PolyArray *sp_PolyArray_new(void) { sp_PolyArray *a = (sp_PolyArray *)sp_gc_alloc(sizeof(sp_PolyArray), sp_PolyArray_fin, sp_PolyArray_scan); a->cap = 16; a->data = (sp_RbVal *)malloc(sizeof(sp_RbVal) * a->cap); a->len = 0; { sp_gc_hdr *h = (sp_gc_hdr *)((char *)a - sizeof(sp_gc_hdr)); h->size += sizeof(sp_RbVal) * a->cap; sp_gc_bytes += sizeof(sp_RbVal) * a->cap; } return a; }
|
|
938
|
+
static void sp_PolyArray_push(sp_PolyArray *a, sp_RbVal v) { if (a->len >= a->cap) { sp_gc_hdr *h = (sp_gc_hdr *)((char *)a - sizeof(sp_gc_hdr)); sp_gc_bytes -= sizeof(sp_RbVal) * a->cap; h->size -= sizeof(sp_RbVal) * a->cap; a->cap = a->cap * 2 + 1; a->data = (sp_RbVal *)realloc(a->data, sizeof(sp_RbVal) * a->cap); h->size += sizeof(sp_RbVal) * a->cap; sp_gc_bytes += sizeof(sp_RbVal) * a->cap; } a->data[a->len++] = v; }
|
|
939
|
+
static sp_RbVal sp_poly_shl(sp_RbVal a, sp_RbVal b) {
|
|
940
|
+
/* Dispatch by recv cls_id: an IntArray / PolyArray / etc. boxed
|
|
941
|
+
into a poly slot still wants Array#<< (push), not Integer#<<
|
|
942
|
+
(bit-shift). Falls through to bit-shift only when the recv is
|
|
943
|
+
a non-array. Returns the recv (matching `<<`s chainability). */
|
|
944
|
+
if (a.tag == SP_TAG_OBJ) {
|
|
945
|
+
if (a.cls_id == SP_BUILTIN_INT_ARRAY) {
|
|
946
|
+
sp_IntArray_push((sp_IntArray *)a.v.p, b.tag == SP_TAG_INT ? b.v.i : sp_poly_to_i(b));
|
|
947
|
+
return a;
|
|
948
|
+
}
|
|
949
|
+
if (a.cls_id == SP_BUILTIN_POLY_ARRAY) {
|
|
950
|
+
sp_PolyArray_push((sp_PolyArray *)a.v.p, b);
|
|
951
|
+
return a;
|
|
952
|
+
}
|
|
953
|
+
if (a.cls_id == SP_BUILTIN_PTR_ARRAY) {
|
|
954
|
+
sp_PtrArray_push((sp_PtrArray *)a.v.p, b.v.p);
|
|
955
|
+
return a;
|
|
956
|
+
}
|
|
957
|
+
if (a.cls_id == SP_BUILTIN_FLT_ARRAY) {
|
|
958
|
+
sp_FloatArray_push((sp_FloatArray *)a.v.p, b.tag == SP_TAG_FLT ? b.v.f : (mrb_float)sp_poly_to_i(b));
|
|
959
|
+
return a;
|
|
960
|
+
}
|
|
961
|
+
if (a.cls_id == SP_BUILTIN_STR_ARRAY) {
|
|
962
|
+
sp_StrArray_push((sp_StrArray *)a.v.p, b.tag == SP_TAG_STR ? (const char *)b.v.p : "");
|
|
963
|
+
return a;
|
|
964
|
+
}
|
|
965
|
+
}
|
|
966
|
+
return sp_box_int(sp_poly_to_i(a) << sp_poly_to_i(b));
|
|
967
|
+
}
|
|
968
|
+
static mrb_int sp_PolyArray_length(sp_PolyArray *a) { return a->len; }
|
|
969
|
+
static sp_RbVal sp_PolyArray_get(sp_PolyArray *a, mrb_int i) { if (i < 0) i += a->len; return a->data[i]; }
|
|
970
|
+
static void sp_PolyArray_set(sp_PolyArray *a, mrb_int i, sp_RbVal v) { if (i < 0) i += a->len; a->data[i] = v; }
|
|
971
|
+
static sp_PolyArray *sp_PolyArray_slice(sp_PolyArray *a, mrb_int start, mrb_int len) { if (start < 0) start += a->len; if (start < 0) start = 0; sp_PolyArray *b = sp_PolyArray_new(); if (start >= a->len || len <= 0) return b; if (start + len > a->len) len = a->len - start; for (mrb_int i = 0; i < len; i++) sp_PolyArray_push(b, a->data[start + i]); return b; }
|
|
972
|
+
static sp_PolyArray *sp_PolyArray_slice_bang(sp_PolyArray *a, mrb_int from, mrb_int n) {
|
|
973
|
+
if (from < 0) from += a->len;
|
|
974
|
+
if (from < 0) from = 0;
|
|
975
|
+
if (from > a->len) from = a->len;
|
|
976
|
+
if (n < 0) n = 0;
|
|
977
|
+
if (from + n > a->len) n = a->len - from;
|
|
978
|
+
sp_PolyArray *r = sp_PolyArray_new();
|
|
979
|
+
for (mrb_int i = 0; i < n; i++) sp_PolyArray_push(r, a->data[from + i]);
|
|
980
|
+
for (mrb_int i = from; i + n < a->len; i++) a->data[i] = a->data[i + n];
|
|
981
|
+
a->len -= n;
|
|
982
|
+
return r;
|
|
983
|
+
}
|
|
984
|
+
static sp_PolyArray *sp_PolyArray_dup(sp_PolyArray *a) { sp_PolyArray *b = sp_PolyArray_new(); for (mrb_int i = 0; i < a->len; i++) sp_PolyArray_push(b, a->data[i]); return b; }
|
|
985
|
+
static void sp_PolyArray_shuffle_bang(sp_PolyArray *a) { for (mrb_int i = a->len - 1; i > 0; i--) { mrb_int j = (mrb_int)(rand() % (i + 1)); sp_RbVal t = a->data[i]; a->data[i] = a->data[j]; a->data[j] = t; } }
|
|
986
|
+
static void sp_PolyArray_rotate_bang(sp_PolyArray*a,mrb_int n){if(a->len<=0)return;n=((n%a->len)+a->len)%a->len;if(n==0)return;sp_RbVal*tmp=(sp_RbVal*)malloc(sizeof(sp_RbVal)*a->len);for(mrb_int i=0;i<a->len;i++)tmp[i]=a->data[(i+n)%a->len];for(mrb_int i=0;i<a->len;i++)a->data[i]=tmp[i];free(tmp);}
|
|
987
|
+
static sp_PolyArray *sp_PolyArray_shuffle(sp_PolyArray *a) { sp_PolyArray *b = sp_PolyArray_dup(a); sp_PolyArray_shuffle_bang(b); return b; }
|
|
988
|
+
|
|
989
|
+
/* Object#inspect for a tagged sp_RbVal. Dispatches on the runtime tag;
|
|
990
|
+
each branch reuses the matching primitive inspect helper. Falls back
|
|
991
|
+
to "#<Object>" for SP_TAG_OBJ because the runtime has no class-name
|
|
992
|
+
table yet (follow-up PR). Returns a GC-managed C string. */
|
|
993
|
+
static const char *sp_poly_inspect(sp_RbVal v) {
|
|
994
|
+
switch (v.tag) {
|
|
995
|
+
case SP_TAG_INT: return sp_int_to_s(v.v.i);
|
|
996
|
+
case SP_TAG_STR: return sp_str_inspect(v.v.s);
|
|
997
|
+
case SP_TAG_FLT: return sp_float_to_s(v.v.f);
|
|
998
|
+
case SP_TAG_BOOL: return v.v.b ? SPL("true") : SPL("false");
|
|
999
|
+
case SP_TAG_NIL: return SPL("nil");
|
|
1000
|
+
case SP_TAG_SYM: return sp_str_concat(":", sp_sym_to_s((sp_sym)v.v.i));
|
|
1001
|
+
case SP_TAG_OBJ: return SPL("#<Object>");
|
|
1002
|
+
default: return sp_str_empty;
|
|
1003
|
+
}
|
|
1004
|
+
}
|
|
1005
|
+
/* Array#inspect for heterogeneous poly arrays. Each element dispatches
|
|
1006
|
+
through sp_poly_inspect, so a mixed `[1, "x", :y]` renders
|
|
1007
|
+
`[1, "x", :y]` byte-for-byte identical to CRuby. */
|
|
1008
|
+
static const char *sp_PolyArray_inspect(sp_PolyArray *a) {
|
|
1009
|
+
sp_String *s = sp_String_new("[");
|
|
1010
|
+
for (mrb_int i = 0; i < a->len; i++) {
|
|
1011
|
+
if (i > 0) sp_String_append(s, ", ");
|
|
1012
|
+
sp_String_append(s, sp_poly_inspect(a->data[i]));
|
|
1013
|
+
}
|
|
1014
|
+
sp_String_append(s, "]");
|
|
1015
|
+
return s->data;
|
|
1016
|
+
}
|
|
1017
|
+
|
|
1018
|
+
/* Mark the embedded GC reference inside an sp_RbVal (string or obj).
|
|
1019
|
+
Used as the scan hook for containers that store polymorphic values. */
|
|
1020
|
+
static inline void sp_mark_rbval(sp_RbVal v) {
|
|
1021
|
+
if (v.tag == SP_TAG_STR) sp_mark_string(v.v.s);
|
|
1022
|
+
else if (v.tag == SP_TAG_OBJ) sp_gc_mark(v.v.p);
|
|
1023
|
+
}
|
|
1024
|
+
|
|
1025
|
+
/* StrPolyHash: string keys, sp_RbVal values — for hashes with mixed value types. */
|
|
1026
|
+
typedef struct{const char**keys;sp_RbVal*vals;const char**order;mrb_int len;mrb_int cap;mrb_int mask;}sp_StrPolyHash;
|
|
1027
|
+
static void sp_StrPolyHash_fin(void*p){sp_StrPolyHash*h=(sp_StrPolyHash*)p;free(h->keys);free(h->vals);free(h->order);}
|
|
1028
|
+
static void sp_StrPolyHash_scan(void*p){sp_StrPolyHash*h=(sp_StrPolyHash*)p;for(mrb_int i=0;i<h->cap;i++){if(h->keys[i]){sp_mark_string(h->keys[i]);sp_mark_rbval(h->vals[i]);}}}
|
|
1029
|
+
static sp_StrPolyHash*sp_StrPolyHash_new(void){sp_StrPolyHash*h=(sp_StrPolyHash*)sp_gc_alloc(sizeof(sp_StrPolyHash),sp_StrPolyHash_fin,sp_StrPolyHash_scan);h->cap=16;h->mask=15;h->keys=(const char**)calloc(h->cap,sizeof(const char*));h->vals=(sp_RbVal*)calloc(h->cap,sizeof(sp_RbVal));h->order=(const char**)malloc(sizeof(const char*)*h->cap);h->len=0;return h;}
|
|
1030
|
+
static void sp_StrPolyHash_grow(sp_StrPolyHash*h){mrb_int oc=h->cap;const char**ok=h->keys;sp_RbVal*ov=h->vals;h->cap*=2;h->mask=h->cap-1;h->keys=(const char**)calloc(h->cap,sizeof(const char*));h->vals=(sp_RbVal*)calloc(h->cap,sizeof(sp_RbVal));h->order=(const char**)realloc(h->order,sizeof(const char*)*h->cap);h->len=0;for(mrb_int i=0;i<oc;i++){if(ok[i]){mrb_int idx=(mrb_int)(sp_str_hash(ok[i])&h->mask);while(h->keys[idx])idx=(idx+1)&h->mask;h->keys[idx]=ok[i];h->vals[idx]=ov[i];h->len++;}}free(ok);free(ov);}
|
|
1031
|
+
static sp_RbVal sp_StrPolyHash_get(sp_StrPolyHash*h,const char*k){mrb_int idx=(mrb_int)(sp_str_hash(k)&h->mask);while(h->keys[idx]){if(strcmp(h->keys[idx],k)==0)return h->vals[idx];idx=(idx+1)&h->mask;}return sp_box_nil();}
|
|
1032
|
+
static void sp_StrPolyHash_set(sp_StrPolyHash*h,const char*k,sp_RbVal v){if(h->len*2>=h->cap)sp_StrPolyHash_grow(h);mrb_int idx=(mrb_int)(sp_str_hash(k)&h->mask);while(h->keys[idx]){if(strcmp(h->keys[idx],k)==0){h->vals[idx]=v;return;}idx=(idx+1)&h->mask;}h->keys[idx]=k;h->vals[idx]=v;h->order[h->len]=k;h->len++;}
|
|
1033
|
+
static mrb_bool sp_StrPolyHash_has_key(sp_StrPolyHash*h,const char*k){mrb_int idx=(mrb_int)(sp_str_hash(k)&h->mask);while(h->keys[idx]){if(strcmp(h->keys[idx],k)==0)return TRUE;idx=(idx+1)&h->mask;}return FALSE;}
|
|
1034
|
+
static mrb_int sp_StrPolyHash_length(sp_StrPolyHash*h){return h->len;}
|
|
1035
|
+
static sp_StrArray*sp_StrPolyHash_keys(sp_StrPolyHash*h){sp_StrArray*a=sp_StrArray_new();for(mrb_int i=0;i<h->len;i++)sp_StrArray_push(a,h->order[i]);return a;}
|
|
1036
|
+
static sp_PolyArray*sp_StrPolyHash_values(sp_StrPolyHash*h){sp_PolyArray*a=sp_PolyArray_new();for(mrb_int i=0;i<h->len;i++)sp_PolyArray_push(a,sp_StrPolyHash_get(h,h->order[i]));return a;}
|
|
1037
|
+
|
|
1038
|
+
/* SymPolyHash: symbol keys, sp_RbVal values — same shape as SymStrHash but with poly values. */
|
|
1039
|
+
typedef struct{sp_sym*keys;sp_RbVal*vals;sp_sym*order;mrb_int len;mrb_int cap;mrb_int mask;}sp_SymPolyHash;
|
|
1040
|
+
static void sp_SymPolyHash_fin(void*p){sp_SymPolyHash*h=(sp_SymPolyHash*)p;free(h->keys);free(h->vals);free(h->order);}
|
|
1041
|
+
static void sp_SymPolyHash_scan(void*p){sp_SymPolyHash*h=(sp_SymPolyHash*)p;for(mrb_int i=0;i<h->cap;i++){if(h->keys[i]>=0)sp_mark_rbval(h->vals[i]);}}
|
|
1042
|
+
static sp_SymPolyHash*sp_SymPolyHash_new(void){sp_SymPolyHash*h=(sp_SymPolyHash*)sp_gc_alloc(sizeof(sp_SymPolyHash),sp_SymPolyHash_fin,sp_SymPolyHash_scan);h->cap=16;h->mask=15;h->keys=(sp_sym*)malloc(sizeof(sp_sym)*h->cap);for(mrb_int i=0;i<h->cap;i++)h->keys[i]=-1;h->vals=(sp_RbVal*)calloc(h->cap,sizeof(sp_RbVal));h->order=(sp_sym*)malloc(sizeof(sp_sym)*h->cap);h->len=0;return h;}
|
|
1043
|
+
static void sp_SymPolyHash_grow(sp_SymPolyHash*h){mrb_int oc=h->cap;sp_sym*ok=h->keys;sp_RbVal*ov=h->vals;h->cap*=2;h->mask=h->cap-1;h->keys=(sp_sym*)malloc(sizeof(sp_sym)*h->cap);for(mrb_int i=0;i<h->cap;i++)h->keys[i]=-1;h->vals=(sp_RbVal*)calloc(h->cap,sizeof(sp_RbVal));h->order=(sp_sym*)realloc(h->order,sizeof(sp_sym)*h->cap);h->len=0;for(mrb_int i=0;i<oc;i++){if(ok[i]>=0){mrb_int idx=(mrb_int)(((mrb_int)ok[i])&h->mask);while(h->keys[idx]>=0)idx=(idx+1)&h->mask;h->keys[idx]=ok[i];h->vals[idx]=ov[i];h->len++;}}free(ok);free(ov);}
|
|
1044
|
+
static sp_RbVal sp_SymPolyHash_get(sp_SymPolyHash*h,sp_sym k){mrb_int idx=(mrb_int)(((mrb_int)k)&h->mask);while(h->keys[idx]>=0){if(h->keys[idx]==k)return h->vals[idx];idx=(idx+1)&h->mask;}return sp_box_nil();}
|
|
1045
|
+
static void sp_SymPolyHash_set(sp_SymPolyHash*h,sp_sym k,sp_RbVal v){if(h->len*2>=h->cap)sp_SymPolyHash_grow(h);mrb_int idx=(mrb_int)(((mrb_int)k)&h->mask);while(h->keys[idx]>=0){if(h->keys[idx]==k){h->vals[idx]=v;return;}idx=(idx+1)&h->mask;}h->keys[idx]=k;h->vals[idx]=v;h->order[h->len]=k;h->len++;}
|
|
1046
|
+
static mrb_bool sp_SymPolyHash_has_key(sp_SymPolyHash*h,sp_sym k){mrb_int idx=(mrb_int)(((mrb_int)k)&h->mask);while(h->keys[idx]>=0){if(h->keys[idx]==k)return TRUE;idx=(idx+1)&h->mask;}return FALSE;}
|
|
1047
|
+
static mrb_int sp_SymPolyHash_length(sp_SymPolyHash*h){return h->len;}
|
|
1048
|
+
static sp_IntArray*sp_SymPolyHash_keys(sp_SymPolyHash*h){sp_IntArray*a=sp_IntArray_new();for(mrb_int i=0;i<h->len;i++)sp_IntArray_push(a,(mrb_int)h->order[i]);return a;}
|
|
1049
|
+
static sp_PolyArray*sp_SymPolyHash_values(sp_SymPolyHash*h){sp_PolyArray*a=sp_PolyArray_new();for(mrb_int i=0;i<h->len;i++)sp_PolyArray_push(a,sp_SymPolyHash_get(h,h->order[i]));return a;}
|
|
1050
|
+
static sp_SymPolyHash*sp_SymPolyHash_merge(sp_SymPolyHash*a,sp_SymPolyHash*b){sp_SymPolyHash*r=sp_SymPolyHash_new();for(mrb_int i=0;i<a->len;i++)sp_SymPolyHash_set(r,a->order[i],sp_SymPolyHash_get(a,a->order[i]));for(mrb_int i=0;i<b->len;i++)sp_SymPolyHash_set(r,b->order[i],sp_SymPolyHash_get(b,b->order[i]));return r;}
|
|
1051
|
+
|
|
1052
|
+
/* PolyPolyHash: heterogeneous keys + values (both sp_RbVal). For
|
|
1053
|
+
primitives the hash/eql is tag-based (value equality); for OBJ tag
|
|
1054
|
+
the default is pointer identity. Codegen patches sp_obj_hash_hook /
|
|
1055
|
+
sp_obj_eql_hook with class-aware overrides (e.g. Method#eql? compares
|
|
1056
|
+
bound receiver + fn_ptr) when the program needs them — null hooks
|
|
1057
|
+
leave the runtime at identity, which is the right default for typed
|
|
1058
|
+
pointers like IntArray. */
|
|
1059
|
+
typedef mrb_int (*sp_obj_hash_fn)(int cls_id, void *p);
|
|
1060
|
+
typedef mrb_bool (*sp_obj_eql_fn)(int cls_id, void *a, void *b);
|
|
1061
|
+
static sp_obj_hash_fn sp_obj_hash_hook = NULL;
|
|
1062
|
+
static sp_obj_eql_fn sp_obj_eql_hook = NULL;
|
|
1063
|
+
static mrb_int sp_rbval_hash_key(sp_RbVal v) {
|
|
1064
|
+
switch (v.tag) {
|
|
1065
|
+
case SP_TAG_INT: case SP_TAG_BOOL: case SP_TAG_NIL: case SP_TAG_SYM:
|
|
1066
|
+
return (mrb_int)v.v.i;
|
|
1067
|
+
case SP_TAG_STR:
|
|
1068
|
+
return v.v.s ? (mrb_int)sp_str_hash(v.v.s) : 0;
|
|
1069
|
+
case SP_TAG_FLT: { uint64_t b; memcpy(&b, &v.v.f, sizeof(b)); return (mrb_int)b; }
|
|
1070
|
+
case SP_TAG_OBJ:
|
|
1071
|
+
if (sp_obj_hash_hook) return sp_obj_hash_hook(v.cls_id, v.v.p);
|
|
1072
|
+
return (mrb_int)((uintptr_t)v.v.p);
|
|
1073
|
+
}
|
|
1074
|
+
return 0;
|
|
1075
|
+
}
|
|
1076
|
+
static mrb_bool sp_rbval_eql_key(sp_RbVal a, sp_RbVal b) {
|
|
1077
|
+
if (a.tag != b.tag) return FALSE;
|
|
1078
|
+
switch (a.tag) {
|
|
1079
|
+
case SP_TAG_INT: case SP_TAG_BOOL: case SP_TAG_NIL: case SP_TAG_SYM:
|
|
1080
|
+
return a.v.i == b.v.i;
|
|
1081
|
+
case SP_TAG_STR:
|
|
1082
|
+
if (a.v.s == b.v.s) return TRUE;
|
|
1083
|
+
if (!a.v.s || !b.v.s) return FALSE;
|
|
1084
|
+
return strcmp(a.v.s, b.v.s) == 0;
|
|
1085
|
+
case SP_TAG_FLT:
|
|
1086
|
+
return a.v.f == b.v.f;
|
|
1087
|
+
case SP_TAG_OBJ:
|
|
1088
|
+
if (a.cls_id != b.cls_id) return FALSE;
|
|
1089
|
+
if (a.v.p == b.v.p) return TRUE;
|
|
1090
|
+
if (sp_obj_eql_hook) return sp_obj_eql_hook(a.cls_id, a.v.p, b.v.p);
|
|
1091
|
+
return FALSE;
|
|
1092
|
+
}
|
|
1093
|
+
return FALSE;
|
|
1094
|
+
}
|
|
1095
|
+
typedef struct{sp_RbVal*keys;sp_RbVal*vals;mrb_int*order;mrb_bool*occ;mrb_int len;mrb_int cap;mrb_int mask;}sp_PolyPolyHash;
|
|
1096
|
+
static void sp_PolyPolyHash_fin(void*p){sp_PolyPolyHash*h=(sp_PolyPolyHash*)p;free(h->keys);free(h->vals);free(h->order);free(h->occ);}
|
|
1097
|
+
static void sp_PolyPolyHash_scan(void*p){sp_PolyPolyHash*h=(sp_PolyPolyHash*)p;for(mrb_int i=0;i<h->cap;i++){if(h->occ[i]){sp_mark_rbval(h->keys[i]);sp_mark_rbval(h->vals[i]);}}}
|
|
1098
|
+
static sp_PolyPolyHash*sp_PolyPolyHash_new(void){sp_PolyPolyHash*h=(sp_PolyPolyHash*)sp_gc_alloc(sizeof(sp_PolyPolyHash),sp_PolyPolyHash_fin,sp_PolyPolyHash_scan);h->cap=16;h->mask=15;h->keys=(sp_RbVal*)calloc(h->cap,sizeof(sp_RbVal));h->vals=(sp_RbVal*)calloc(h->cap,sizeof(sp_RbVal));h->order=(mrb_int*)malloc(sizeof(mrb_int)*h->cap);h->occ=(mrb_bool*)calloc(h->cap,sizeof(mrb_bool));h->len=0;return h;}
|
|
1099
|
+
static void sp_PolyPolyHash_grow(sp_PolyPolyHash*h){mrb_int oc=h->cap;sp_RbVal*ok=h->keys;sp_RbVal*ov=h->vals;mrb_bool*oo=h->occ;mrb_int*oord=h->order;mrb_int olen=h->len;h->cap*=2;h->mask=h->cap-1;h->keys=(sp_RbVal*)calloc(h->cap,sizeof(sp_RbVal));h->vals=(sp_RbVal*)calloc(h->cap,sizeof(sp_RbVal));h->order=(mrb_int*)malloc(sizeof(mrb_int)*h->cap);h->occ=(mrb_bool*)calloc(h->cap,sizeof(mrb_bool));for(mrb_int i=0;i<olen;i++){mrb_int oi=oord[i];sp_RbVal k=ok[oi];mrb_int idx=(mrb_int)(sp_rbval_hash_key(k)&h->mask);while(h->occ[idx])idx=(idx+1)&h->mask;h->keys[idx]=k;h->vals[idx]=ov[oi];h->occ[idx]=TRUE;h->order[i]=idx;}free(ok);free(ov);free(oo);free(oord);}
|
|
1100
|
+
static sp_RbVal sp_PolyPolyHash_get(sp_PolyPolyHash*h,sp_RbVal k){mrb_int idx=(mrb_int)(sp_rbval_hash_key(k)&h->mask);while(h->occ[idx]){if(sp_rbval_eql_key(h->keys[idx],k))return h->vals[idx];idx=(idx+1)&h->mask;}return sp_box_nil();}
|
|
1101
|
+
static void sp_PolyPolyHash_set(sp_PolyPolyHash*h,sp_RbVal k,sp_RbVal v){if(h->len*2>=h->cap)sp_PolyPolyHash_grow(h);mrb_int idx=(mrb_int)(sp_rbval_hash_key(k)&h->mask);while(h->occ[idx]){if(sp_rbval_eql_key(h->keys[idx],k)){h->vals[idx]=v;return;}idx=(idx+1)&h->mask;}h->keys[idx]=k;h->vals[idx]=v;h->occ[idx]=TRUE;h->order[h->len]=idx;h->len++;}
|
|
1102
|
+
static mrb_bool sp_PolyPolyHash_has_key(sp_PolyPolyHash*h,sp_RbVal k){mrb_int idx=(mrb_int)(sp_rbval_hash_key(k)&h->mask);while(h->occ[idx]){if(sp_rbval_eql_key(h->keys[idx],k))return TRUE;idx=(idx+1)&h->mask;}return FALSE;}
|
|
1103
|
+
static mrb_int sp_PolyPolyHash_length(sp_PolyPolyHash*h){return h->len;}
|
|
1104
|
+
static sp_PolyArray*sp_PolyPolyHash_keys(sp_PolyPolyHash*h){sp_PolyArray*a=sp_PolyArray_new();for(mrb_int i=0;i<h->len;i++)sp_PolyArray_push(a,h->keys[h->order[i]]);return a;}
|
|
1105
|
+
static sp_PolyArray*sp_PolyPolyHash_values(sp_PolyPolyHash*h){sp_PolyArray*a=sp_PolyArray_new();for(mrb_int i=0;i<h->len;i++)sp_PolyArray_push(a,h->vals[h->order[i]]);return a;}
|
|
1106
|
+
|
|
1107
|
+
#include <setjmp.h>
|
|
1108
|
+
#define SP_EXC_STACK_MAX 64
|
|
1109
|
+
static jmp_buf sp_exc_stack[SP_EXC_STACK_MAX];
|
|
1110
|
+
static const char *sp_exc_msg[SP_EXC_STACK_MAX];
|
|
1111
|
+
static volatile int sp_exc_top = 0;
|
|
1112
|
+
static const char *sp_exc_cls[SP_EXC_STACK_MAX];
|
|
1113
|
+
static volatile const char *sp_last_exc_cls = "";
|
|
1114
|
+
static void sp_raise_cls(const char *cls, const char *msg) { if (sp_exc_top > 0) { sp_exc_msg[sp_exc_top-1] = msg; sp_exc_cls[sp_exc_top-1] = cls; sp_last_exc_cls = cls; longjmp(sp_exc_stack[sp_exc_top-1], 1); } fprintf(stderr, "unhandled exception: %s\n", msg); exit(1); }
|
|
1115
|
+
static void sp_raise(const char *msg) { sp_raise_cls("RuntimeError", msg); }
|
|
1116
|
+
|
|
1117
|
+
/* Cross-TU bridge for sp_bigint.c (compiled as a separate translation
|
|
1118
|
+
unit; can't see static helpers in this header). Defined non-static
|
|
1119
|
+
so sp_bigint.c's mrb_raise macro can dispatch into spinel's
|
|
1120
|
+
longjmp-based rescue net rather than fprintf+exit. */
|
|
1121
|
+
void sp_bigint_raise_zerodiv(const char *msg) { sp_raise_cls("ZeroDivisionError", msg); }
|
|
1122
|
+
static mrb_bool sp_exc_is_a(const char *cls, const char *target) { return strcmp(cls, target) == 0; }
|
|
1123
|
+
|
|
1124
|
+
#define SP_CATCH_STACK_MAX 64
|
|
1125
|
+
static jmp_buf sp_catch_stack[SP_CATCH_STACK_MAX];
|
|
1126
|
+
static const char *sp_catch_tag[SP_CATCH_STACK_MAX];
|
|
1127
|
+
static mrb_int sp_catch_val[SP_CATCH_STACK_MAX];
|
|
1128
|
+
static volatile int sp_catch_top = 0;
|
|
1129
|
+
static void sp_throw(const char *tag, mrb_int val) { int i = sp_catch_top - 1; while (i >= 0) { if (strcmp(sp_catch_tag[i], tag) == 0) { sp_catch_val[i] = val; sp_catch_top = i + 1; longjmp(sp_catch_stack[i], 1); } i--; } fprintf(stderr, "uncaught throw: %s\n", tag); exit(1); }
|
|
1130
|
+
|
|
1131
|
+
/* Text mode ("r") matches CRuby's File.read: on Windows, CRLF is
|
|
1132
|
+
normalized to LF on read, which cancels out fopen("w")'s
|
|
1133
|
+
LF→CRLF on write. Without this, content from File.read passed to
|
|
1134
|
+
puts goes through stdout's text-mode translation a second time
|
|
1135
|
+
and `\r\n` becomes `\r\r\n`. fread's actual byte count drives
|
|
1136
|
+
null-termination because text mode shrinks the byte count below
|
|
1137
|
+
ftell's raw-file size. */
|
|
1138
|
+
static const char *sp_file_read(const char *path) { FILE *f = fopen(path, "r"); if (!f) return &("\xff" "")[1]; fseek(f, 0, SEEK_END); long sz = ftell(f); fseek(f, 0, SEEK_SET); char *buf = sp_str_alloc(sz); size_t n = 0; if (sz > 0) { n = fread(buf, 1, sz, f); } buf[n] = 0; fclose(f); return buf; }
|
|
1139
|
+
static void sp_file_write(const char *path, const char *data) { FILE *f = fopen(path, "w"); if (f) { fputs(data, f); fclose(f); } }
|
|
1140
|
+
static mrb_bool sp_file_exist(const char *path) { FILE *f = fopen(path, "r"); if (f) { fclose(f); return TRUE; } return FALSE; }
|
|
1141
|
+
static void sp_file_delete(const char *path) { remove(path); }
|
|
1142
|
+
static const char *sp_backtick(const char *cmd) { FILE *p = popen(cmd, "r"); if (!p) return sp_str_empty; char *buf = sp_str_alloc_raw(4096); size_t n = fread(buf, 1, 4095, p); buf[n] = 0; pclose(p); return buf; }
|
|
1143
|
+
static const char *sp_file_basename(const char *path) {
|
|
1144
|
+
const char *s = strrchr(path, '/');
|
|
1145
|
+
const char *base = s ? s + 1 : path;
|
|
1146
|
+
/* sp_gc_mark looks at byte[-1] to distinguish heap strings (`\xfe`)
|
|
1147
|
+
from literals (`\xff`). A `s+1` mid-path pointer has whatever the
|
|
1148
|
+
'/' was before it — and for an arbitrary string that's not a tag,
|
|
1149
|
+
so the GC tries to dereference it as a heap header and segfaults.
|
|
1150
|
+
Return a fresh sp_str_alloc'd copy so the prefix marker is right. */
|
|
1151
|
+
size_t n = strlen(base);
|
|
1152
|
+
char *buf = sp_str_alloc(n);
|
|
1153
|
+
memcpy(buf, base, n + 1);
|
|
1154
|
+
return buf;
|
|
1155
|
+
}
|
|
1156
|
+
|
|
1157
|
+
/* Read a file's bytes into a fresh IntArray. Distinct from
|
|
1158
|
+
`sp_str_bytes(sp_file_read(path))` because plain sp_str_bytes uses
|
|
1159
|
+
null-termination and stops at the first 0x00 byte — wrong for
|
|
1160
|
+
binary data (e.g. .nes ROM files). */
|
|
1161
|
+
static sp_IntArray *sp_file_binread_bytes(const char *path) {
|
|
1162
|
+
FILE *f = fopen(path, "rb");
|
|
1163
|
+
sp_IntArray *a = sp_IntArray_new();
|
|
1164
|
+
if (!f) return a;
|
|
1165
|
+
fseek(f, 0, SEEK_END);
|
|
1166
|
+
long sz = ftell(f);
|
|
1167
|
+
fseek(f, 0, SEEK_SET);
|
|
1168
|
+
unsigned char *buf = (unsigned char *)malloc(sz > 0 ? (size_t)sz : 1);
|
|
1169
|
+
if (buf && sz > 0) {
|
|
1170
|
+
/* Use fread's actual byte count, not the raw file size — a
|
|
1171
|
+
partial read otherwise pushes uninitialized memory. */
|
|
1172
|
+
size_t r = fread(buf, 1, (size_t)sz, f);
|
|
1173
|
+
for (size_t i = 0; i < r; i++) sp_IntArray_push(a, (mrb_int)buf[i]);
|
|
1174
|
+
}
|
|
1175
|
+
free(buf);
|
|
1176
|
+
fclose(f);
|
|
1177
|
+
return a;
|
|
1178
|
+
}
|
|
1179
|
+
|
|
1180
|
+
/* `arr.slice!(from, n)` — returns a fresh array of `n` elements
|
|
1181
|
+
starting at `from` and removes them from `a`. IntArray uses its
|
|
1182
|
+
`start` field for an O(1) head peel (from == 0); the others
|
|
1183
|
+
shift the tail down to fill the hole. */
|
|
1184
|
+
static sp_IntArray *sp_IntArray_slice_bang(sp_IntArray *a, mrb_int from, mrb_int n) {
|
|
1185
|
+
if (from < 0) from += a->len;
|
|
1186
|
+
if (from < 0) from = 0;
|
|
1187
|
+
if (from > a->len) from = a->len;
|
|
1188
|
+
if (n < 0) n = 0;
|
|
1189
|
+
if (from + n > a->len) n = a->len - from;
|
|
1190
|
+
sp_IntArray *r = sp_IntArray_new();
|
|
1191
|
+
for (mrb_int i = 0; i < n; i++) sp_IntArray_push(r, a->data[a->start + from + i]);
|
|
1192
|
+
if (from == 0) {
|
|
1193
|
+
a->start += n;
|
|
1194
|
+
a->len -= n;
|
|
1195
|
+
} else {
|
|
1196
|
+
for (mrb_int i = from; i + n < a->len; i++) a->data[a->start + i] = a->data[a->start + i + n];
|
|
1197
|
+
a->len -= n;
|
|
1198
|
+
}
|
|
1199
|
+
return r;
|
|
1200
|
+
}
|
|
1201
|
+
static sp_FloatArray *sp_FloatArray_slice_bang(sp_FloatArray *a, mrb_int from, mrb_int n) {
|
|
1202
|
+
if (from < 0) from += a->len;
|
|
1203
|
+
if (from < 0) from = 0;
|
|
1204
|
+
if (from > a->len) from = a->len;
|
|
1205
|
+
if (n < 0) n = 0;
|
|
1206
|
+
if (from + n > a->len) n = a->len - from;
|
|
1207
|
+
sp_FloatArray *r = sp_FloatArray_new();
|
|
1208
|
+
for (mrb_int i = 0; i < n; i++) sp_FloatArray_push(r, a->data[from + i]);
|
|
1209
|
+
for (mrb_int i = from; i + n < a->len; i++) a->data[i] = a->data[i + n];
|
|
1210
|
+
a->len -= n;
|
|
1211
|
+
return r;
|
|
1212
|
+
}
|
|
1213
|
+
static sp_StrArray *sp_StrArray_slice_bang(sp_StrArray *a, mrb_int from, mrb_int n) {
|
|
1214
|
+
if (from < 0) from += a->len;
|
|
1215
|
+
if (from < 0) from = 0;
|
|
1216
|
+
if (from > a->len) from = a->len;
|
|
1217
|
+
if (n < 0) n = 0;
|
|
1218
|
+
if (from + n > a->len) n = a->len - from;
|
|
1219
|
+
sp_StrArray *r = sp_StrArray_new();
|
|
1220
|
+
for (mrb_int i = 0; i < n; i++) sp_StrArray_push(r, a->data[from + i]);
|
|
1221
|
+
for (mrb_int i = from; i + n < a->len; i++) a->data[i] = a->data[i + n];
|
|
1222
|
+
a->len -= n;
|
|
1223
|
+
return r;
|
|
1224
|
+
}
|
|
1225
|
+
static sp_PtrArray *sp_PtrArray_slice_bang(sp_PtrArray *a, mrb_int from, mrb_int n) {
|
|
1226
|
+
if (from < 0) from += a->len;
|
|
1227
|
+
if (from < 0) from = 0;
|
|
1228
|
+
if (from > a->len) from = a->len;
|
|
1229
|
+
if (n < 0) n = 0;
|
|
1230
|
+
if (from + n > a->len) n = a->len - from;
|
|
1231
|
+
sp_PtrArray *r = sp_PtrArray_new_scan(a->scan_elem);
|
|
1232
|
+
for (mrb_int i = 0; i < n; i++) sp_PtrArray_push(r, a->data[from + i]);
|
|
1233
|
+
for (mrb_int i = from; i + n < a->len; i++) a->data[i] = a->data[i + n];
|
|
1234
|
+
a->len -= n;
|
|
1235
|
+
return r;
|
|
1236
|
+
}
|
|
1237
|
+
|
|
1238
|
+
typedef struct sp_Proc { void *fn; void *cap; void (*cap_scan)(void *); } sp_Proc;
|
|
1239
|
+
static void sp_Proc_scan(void *p) { sp_Proc *pr = (sp_Proc *)p; if (pr->cap && pr->cap_scan) pr->cap_scan(pr->cap); }
|
|
1240
|
+
static sp_Proc *sp_proc_new(void *fn, void *cap, void (*cap_scan)(void *)) { sp_Proc *p = (sp_Proc *)sp_gc_alloc(sizeof(sp_Proc), NULL, sp_Proc_scan); p->fn = fn; p->cap = cap; p->cap_scan = cap_scan; return p; }
|
|
1241
|
+
static mrb_int sp_proc_call(sp_Proc *p, mrb_int *args) { return (p && p->fn) ? ((mrb_int (*)(void *, mrb_int *))p->fn)(p->cap, args) : 0; }
|
|
1242
|
+
|
|
1243
|
+
/* ---- StringIO runtime ---- */
|
|
1244
|
+
typedef struct { char *buf; int64_t len; int64_t cap; int64_t pos; int64_t lineno; int closed; } sp_StringIO;
|
|
1245
|
+
static void sio_grow(sp_StringIO *sio, int64_t need) { int64_t req = sio->pos + need; if (req <= sio->cap) return; int64_t nc = sio->cap ? sio->cap : 64; while (nc < req) nc *= 2; sio->buf = (char *)realloc(sio->buf, nc + 1); sio->cap = nc; }
|
|
1246
|
+
static int64_t sio_write(sp_StringIO *sio, const char *d, int64_t dl) { sio_grow(sio, dl); if (sio->pos > sio->len) memset(sio->buf + sio->len, 0, sio->pos - sio->len); memcpy(sio->buf + sio->pos, d, dl); sio->pos += dl; if (sio->pos > sio->len) sio->len = sio->pos; sio->buf[sio->len] = '\0'; return dl; }
|
|
1247
|
+
static sp_StringIO *sp_StringIO_new(void) { sp_StringIO *s = (sp_StringIO *)calloc(1, sizeof(sp_StringIO)); s->buf = (char *)calloc(1, 64); s->cap = 63; return s; }
|
|
1248
|
+
static sp_StringIO *sp_StringIO_new_s(const char *init) { sp_StringIO *s = (sp_StringIO *)calloc(1, sizeof(sp_StringIO)); int64_t l = (int64_t)strlen(init); int64_t c = l < 63 ? 63 : l; s->buf = (char*)malloc(c+1); memcpy(s->buf, init, l); s->buf[l]='\0'; s->len = l; s->cap = c; return s; }
|
|
1249
|
+
static const char *sp_StringIO_string(sp_StringIO *s) { return s->buf ? s->buf : ""; }
|
|
1250
|
+
static int64_t sp_StringIO_pos(sp_StringIO *s) { return s->pos; }
|
|
1251
|
+
static int64_t sp_StringIO_size(sp_StringIO *s) { return s->len; }
|
|
1252
|
+
static int64_t sp_StringIO_write(sp_StringIO *s, const char *str) { return sio_write(s, str, (int64_t)strlen(str)); }
|
|
1253
|
+
static int64_t sp_StringIO_puts(sp_StringIO *s, const char *str) { int64_t l = (int64_t)strlen(str); sio_write(s, str, l); if (l == 0 || str[l-1] != '\n') sio_write(s, "\n", 1); return 0; }
|
|
1254
|
+
static int64_t sp_StringIO_puts_empty(sp_StringIO *s) { sio_write(s, "\n", 1); return 0; }
|
|
1255
|
+
static int64_t sp_StringIO_print(sp_StringIO *s, const char *str) { return sio_write(s, str, (int64_t)strlen(str)); }
|
|
1256
|
+
static int64_t sp_StringIO_putc(sp_StringIO *s, int64_t ch) { char c = (char)(ch & 0xFF); sio_write(s, &c, 1); return ch; }
|
|
1257
|
+
static const char *sp_StringIO_read(sp_StringIO *s) { if (s->pos >= s->len) return sp_str_empty; size_t rem = s->len - s->pos; char *r = sp_str_alloc(rem); memcpy(r, s->buf + s->pos, rem); r[rem] = 0; s->pos = s->len; return r; }
|
|
1258
|
+
static const char *sp_StringIO_read_n(sp_StringIO *s, int64_t n) { if (s->pos >= s->len) return sp_str_empty; int64_t rem = s->len - s->pos; if (n > rem) n = rem; char *r = sp_str_alloc_raw(n+1); memcpy(r, s->buf + s->pos, n); r[n] = '\0'; s->pos += n; return r; }
|
|
1259
|
+
static const char *sp_StringIO_gets(sp_StringIO *s) { if (s->pos >= s->len) return NULL; const char *st = s->buf + s->pos; const char *nl = memchr(st, '\n', s->len - s->pos); int64_t ll = nl ? (nl - st) + 1 : s->len - s->pos; char *r = sp_str_alloc_raw(ll+1); memcpy(r, st, ll); r[ll] = '\0'; s->pos += ll; s->lineno++; return r; }
|
|
1260
|
+
static const char *sp_StringIO_getc(sp_StringIO *s) { if (s->pos >= s->len) return NULL; char *gc = sp_str_alloc_raw(2); gc[0] = s->buf[s->pos++]; gc[1] = '\0'; return gc; }
|
|
1261
|
+
static int64_t sp_StringIO_getbyte(sp_StringIO *s) { if (s->pos >= s->len) return -1; return (int64_t)(unsigned char)s->buf[s->pos++]; }
|
|
1262
|
+
static int64_t sp_StringIO_rewind(sp_StringIO *s) { s->pos = 0; s->lineno = 0; return 0; }
|
|
1263
|
+
static int64_t sp_StringIO_seek(sp_StringIO *s, int64_t off) { if (off < 0) off = 0; s->pos = off; return 0; }
|
|
1264
|
+
static int64_t sp_StringIO_tell(sp_StringIO *s) { return s->pos; }
|
|
1265
|
+
static mrb_bool sp_StringIO_eof_p(sp_StringIO *s) { return s->pos >= s->len; }
|
|
1266
|
+
static int64_t sp_StringIO_truncate(sp_StringIO *s, int64_t l) { if (l < 0) l = 0; if (l < s->len) { s->len = l; s->buf[l] = '\0'; } return 0; }
|
|
1267
|
+
static int64_t sp_StringIO_close(sp_StringIO *s) { s->closed = 1; return 0; }
|
|
1268
|
+
static mrb_bool sp_StringIO_closed_p(sp_StringIO *s) { return s->closed; }
|
|
1269
|
+
static sp_StringIO *sp_StringIO_flush(sp_StringIO *s) { return s; }
|
|
1270
|
+
static mrb_bool sp_StringIO_sync(sp_StringIO *s) { (void)s; return 1; }
|
|
1271
|
+
static mrb_bool sp_StringIO_isatty(sp_StringIO *s) { (void)s; return 0; }
|
|
1272
|
+
|
|
1273
|
+
/* ---- Lambda/closure runtime (sp_Val) ---- */
|
|
1274
|
+
typedef struct sp_Val sp_Val;
|
|
1275
|
+
typedef sp_Val *(*sp_fn_t)(sp_Val *self, sp_Val *arg);
|
|
1276
|
+
struct sp_Val { enum { SP_PROC2, SP_INT2, SP_BOOL2, SP_NIL2 } tag; union { struct { sp_fn_t fn; int ncaptures; } proc; mrb_int ival; mrb_bool bval; } u; sp_Val *captures[]; };
|
|
1277
|
+
#ifdef _WIN32
|
|
1278
|
+
#define SP_ARENA_SIZE ((size_t)256ULL * 1024 * 1024)
|
|
1279
|
+
#else
|
|
1280
|
+
#define SP_ARENA_SIZE ((size_t)16ULL * 1024 * 1024 * 1024)
|
|
1281
|
+
#endif
|
|
1282
|
+
static char *sp_arena = NULL; static size_t sp_arena_pos = 0;
|
|
1283
|
+
static void *sp_lam_alloc(size_t sz) { sz = (sz + 7) & ~(size_t)7; if (!sp_arena) { sp_arena = (char *)mmap(NULL, SP_ARENA_SIZE, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_ANONYMOUS|MAP_NORESERVE, -1, 0); if (sp_arena == MAP_FAILED) { perror("mmap"); exit(1); } sp_arena_pos = 0; } if (sp_arena_pos + sz > SP_ARENA_SIZE) { fprintf(stderr, "arena exhausted\n"); exit(1); } void *p = sp_arena + sp_arena_pos; sp_arena_pos += sz; return p; }
|
|
1284
|
+
static sp_Val *sp_lam_proc(sp_fn_t fn, int ncap) { sp_Val *v = (sp_Val *)sp_lam_alloc(sizeof(sp_Val) + sizeof(sp_Val *) * ncap); v->tag = SP_PROC2; v->u.proc.fn = fn; v->u.proc.ncaptures = ncap; return v; }
|
|
1285
|
+
static sp_Val *sp_lam_int(mrb_int n) { sp_Val *v = (sp_Val *)sp_lam_alloc(sizeof(sp_Val)); v->tag = SP_INT2; v->u.ival = n; return v; }
|
|
1286
|
+
static sp_Val *sp_lam_bool(mrb_bool b) { sp_Val *v = (sp_Val *)sp_lam_alloc(sizeof(sp_Val)); v->tag = SP_BOOL2; v->u.bval = b; return v; }
|
|
1287
|
+
static sp_Val sp_lam_nil_val = { .tag = SP_NIL2 };
|
|
1288
|
+
static sp_Val *sp_lam_call(sp_Val *f, sp_Val *arg) { return f->u.proc.fn(f, arg); }
|
|
1289
|
+
static mrb_int sp_lam_to_int(sp_Val *v) { return v->u.ival; }
|
|
1290
|
+
|
|
1291
|
+
/* ---- Fiber runtime (ucontext) ---- */
|
|
1292
|
+
#ifdef _WIN32
|
|
1293
|
+
#define SP_FIBER_STACK_SIZE (64*1024)
|
|
1294
|
+
typedef struct sp_Fiber{LPVOID ctx;LPVOID caller_ctx;char*stack;int state;int transferred;sp_RbVal yielded_value;sp_RbVal resumed_value;void(*body)(struct sp_Fiber*);void*user_data;int saved_exc_top;int saved_catch_top;struct sp_Fiber*caller;}sp_Fiber;
|
|
1295
|
+
static sp_Fiber sp_fiber_root;
|
|
1296
|
+
static sp_Fiber*sp_fiber_current=&sp_fiber_root;
|
|
1297
|
+
static void sp_Fiber_fin(void*p){sp_Fiber*f=(sp_Fiber*)p;if(f->ctx)DeleteFiber(f->ctx);}
|
|
1298
|
+
static void sp_Fiber_scan(void*p){sp_Fiber*f=(sp_Fiber*)p;if(f->user_data)sp_gc_mark(f->user_data);}
|
|
1299
|
+
static sp_Fiber*sp_Fiber_new(void(*body)(sp_Fiber*)){sp_Fiber*f=(sp_Fiber*)sp_gc_alloc(sizeof(sp_Fiber),sp_Fiber_fin,sp_Fiber_scan);f->ctx=NULL;f->caller_ctx=NULL;f->stack=NULL;f->state=0;f->transferred=0;f->body=body;f->yielded_value=sp_box_nil();f->resumed_value=sp_box_nil();f->user_data=NULL;f->saved_exc_top=0;f->saved_catch_top=0;f->caller=NULL;return f;}
|
|
1300
|
+
static VOID CALLBACK sp_fiber_trampoline(LPVOID param){sp_Fiber*f=(sp_Fiber*)param;f->body(f);f->state=3;if(f->transferred){sp_fiber_current=&sp_fiber_root;SwitchToFiber(sp_fiber_root.ctx);}else{SwitchToFiber(f->caller->ctx);}}
|
|
1301
|
+
static void sp_fiber_ensure_root(void){if(!sp_fiber_root.ctx){sp_fiber_root.ctx=ConvertThreadToFiber(NULL);if(!sp_fiber_root.ctx)sp_fiber_root.ctx=GetCurrentFiber();}}
|
|
1302
|
+
static sp_RbVal sp_Fiber_resume(sp_Fiber*f,sp_RbVal val){if(f->state==3){sp_raise_cls("FiberError","attempt to resume a terminated fiber");}sp_fiber_ensure_root();f->resumed_value=val;sp_Fiber*prev=sp_fiber_current;f->caller=prev;sp_fiber_current=f;if(f->state==0){f->state=1;f->ctx=CreateFiber(SP_FIBER_STACK_SIZE,sp_fiber_trampoline,f);if(!f->ctx)sp_raise("failed to create fiber");}else{f->state=1;}SwitchToFiber(f->ctx);sp_fiber_current=prev;return f->yielded_value;}
|
|
1303
|
+
static sp_RbVal sp_Fiber_yield(sp_RbVal val){sp_Fiber*f=sp_fiber_current;f->yielded_value=val;f->state=2;SwitchToFiber(f->caller->ctx);return f->resumed_value;}
|
|
1304
|
+
static mrb_bool sp_Fiber_alive(sp_Fiber*f){return f->state!=3;}
|
|
1305
|
+
static sp_RbVal sp_Fiber_transfer(sp_Fiber*f,sp_RbVal val){sp_fiber_ensure_root();f->resumed_value=val;sp_Fiber*prev=sp_fiber_current;sp_fiber_current=f;if(f->state==0){f->state=1;f->transferred=1;f->caller=prev;f->ctx=CreateFiber(SP_FIBER_STACK_SIZE,sp_fiber_trampoline,f);if(!f->ctx)sp_raise("failed to create fiber");}else{f->state=1;}SwitchToFiber(f->ctx);sp_fiber_current=prev;return prev->resumed_value;}
|
|
1306
|
+
#else
|
|
1307
|
+
#include <ucontext.h>
|
|
1308
|
+
#include <sys/mman.h>
|
|
1309
|
+
#define SP_FIBER_STACK_SIZE (64*1024)
|
|
1310
|
+
typedef struct sp_Fiber{ucontext_t ctx;ucontext_t caller_ctx;char*stack;int state;int transferred;sp_RbVal yielded_value;sp_RbVal resumed_value;void(*body)(struct sp_Fiber*);void*user_data;int saved_exc_top;int saved_catch_top;}sp_Fiber;
|
|
1311
|
+
static sp_Fiber sp_fiber_root;
|
|
1312
|
+
static sp_Fiber*sp_fiber_current=&sp_fiber_root;
|
|
1313
|
+
static void sp_Fiber_fin(void*p){sp_Fiber*f=(sp_Fiber*)p;if(f->stack)munmap(f->stack,SP_FIBER_STACK_SIZE);}
|
|
1314
|
+
static void sp_Fiber_scan(void*p){sp_Fiber*f=(sp_Fiber*)p;if(f->user_data)sp_gc_mark(f->user_data);}
|
|
1315
|
+
static sp_Fiber*sp_Fiber_new(void(*body)(sp_Fiber*)){sp_Fiber*f=(sp_Fiber*)sp_gc_alloc(sizeof(sp_Fiber),sp_Fiber_fin,sp_Fiber_scan);f->stack=(char*)mmap(NULL,SP_FIBER_STACK_SIZE,PROT_READ|PROT_WRITE,MAP_PRIVATE|MAP_ANONYMOUS,-1,0);f->state=0;f->transferred=0;f->body=body;f->yielded_value=sp_box_nil();f->resumed_value=sp_box_nil();f->user_data=NULL;f->saved_exc_top=0;f->saved_catch_top=0;return f;}
|
|
1316
|
+
static void sp_fiber_trampoline(void){sp_Fiber*f=sp_fiber_current;f->body(f);f->state=3;if(f->transferred){sp_fiber_current=&sp_fiber_root;setcontext(&sp_fiber_root.ctx);}else{swapcontext(&f->ctx,&f->caller_ctx);}}
|
|
1317
|
+
static sp_RbVal sp_Fiber_resume(sp_Fiber*f,sp_RbVal val){if(f->state==3){sp_raise_cls("FiberError","attempt to resume a terminated fiber");}f->resumed_value=val;sp_Fiber*prev=sp_fiber_current;sp_fiber_current=f;if(f->state==0){f->state=1;getcontext(&f->ctx);f->ctx.uc_stack.ss_sp=f->stack;f->ctx.uc_stack.ss_size=SP_FIBER_STACK_SIZE;f->ctx.uc_link=&f->caller_ctx;makecontext(&f->ctx,(void(*)(void))sp_fiber_trampoline,0);swapcontext(&f->caller_ctx,&f->ctx);}else{f->state=1;swapcontext(&f->caller_ctx,&f->ctx);}sp_fiber_current=prev;return f->yielded_value;}
|
|
1318
|
+
static sp_RbVal sp_Fiber_yield(sp_RbVal val){sp_Fiber*f=sp_fiber_current;f->yielded_value=val;f->state=2;swapcontext(&f->ctx,&f->caller_ctx);return f->resumed_value;}
|
|
1319
|
+
static mrb_bool sp_Fiber_alive(sp_Fiber*f){return f->state!=3;}
|
|
1320
|
+
static sp_RbVal sp_Fiber_transfer(sp_Fiber*f,sp_RbVal val){f->resumed_value=val;sp_Fiber*prev=sp_fiber_current;sp_fiber_current=f;if(f->state==0){f->state=1;f->transferred=1;getcontext(&f->ctx);f->ctx.uc_stack.ss_sp=f->stack;f->ctx.uc_stack.ss_size=SP_FIBER_STACK_SIZE;f->ctx.uc_link=&prev->ctx;makecontext(&f->ctx,(void(*)(void))sp_fiber_trampoline,0);swapcontext(&prev->ctx,&f->ctx);}else{f->state=1;swapcontext(&prev->ctx,&f->ctx);}sp_fiber_current=prev;return prev->resumed_value;}
|
|
1321
|
+
#endif
|
|
1322
|
+
|
|
1323
|
+
/* Bigint (linked from sp_bigint.o) */
|
|
1324
|
+
typedef struct sp_Bigint sp_Bigint;
|
|
1325
|
+
sp_Bigint *sp_bigint_new_int(int64_t v);
|
|
1326
|
+
sp_Bigint *sp_bigint_new_str(const char *s, int base);
|
|
1327
|
+
sp_Bigint *sp_bigint_add(sp_Bigint *a, sp_Bigint *b);
|
|
1328
|
+
sp_Bigint *sp_bigint_sub(sp_Bigint *a, sp_Bigint *b);
|
|
1329
|
+
sp_Bigint *sp_bigint_mul(sp_Bigint *a, sp_Bigint *b);
|
|
1330
|
+
sp_Bigint *sp_bigint_div(sp_Bigint *a, sp_Bigint *b);
|
|
1331
|
+
sp_Bigint *sp_bigint_mod(sp_Bigint *a, sp_Bigint *b);
|
|
1332
|
+
sp_Bigint *sp_bigint_pow(sp_Bigint *base, int64_t exp);
|
|
1333
|
+
int sp_bigint_cmp(sp_Bigint *a, sp_Bigint *b);
|
|
1334
|
+
int64_t sp_bigint_to_int(sp_Bigint *b);
|
|
1335
|
+
const char *sp_bigint_to_s(sp_Bigint *b);
|
|
1336
|
+
void sp_bigint_free(sp_Bigint *b);
|
|
1337
|
+
|
|
1338
|
+
|
|
1339
|
+
/* System/backtick support */
|
|
1340
|
+
static int sp_last_status = 0;
|
|
1341
|
+
|
|
1342
|
+
/* Bigint (linked from libspinel_rt.a) */
|
|
1343
|
+
typedef struct sp_Bigint sp_Bigint;
|
|
1344
|
+
sp_Bigint *sp_bigint_new_int(int64_t v);
|
|
1345
|
+
sp_Bigint *sp_bigint_new_str(const char *s, int base);
|
|
1346
|
+
sp_Bigint *sp_bigint_add(sp_Bigint *a, sp_Bigint *b);
|
|
1347
|
+
sp_Bigint *sp_bigint_sub(sp_Bigint *a, sp_Bigint *b);
|
|
1348
|
+
sp_Bigint *sp_bigint_mul(sp_Bigint *a, sp_Bigint *b);
|
|
1349
|
+
sp_Bigint *sp_bigint_div(sp_Bigint *a, sp_Bigint *b);
|
|
1350
|
+
sp_Bigint *sp_bigint_mod(sp_Bigint *a, sp_Bigint *b);
|
|
1351
|
+
sp_Bigint *sp_bigint_pow(sp_Bigint *base, int64_t exp);
|
|
1352
|
+
int sp_bigint_cmp(sp_Bigint *a, sp_Bigint *b);
|
|
1353
|
+
int64_t sp_bigint_to_int(sp_Bigint *b);
|
|
1354
|
+
const char *sp_bigint_to_s(sp_Bigint *b);
|
|
1355
|
+
void sp_bigint_free(sp_Bigint *b);
|
|
1356
|
+
|
|
1357
|
+
#ifdef __APPLE__
|
|
1358
|
+
#pragma clang diagnostic pop
|
|
1359
|
+
#endif
|
|
1360
|
+
|
|
1361
|
+
#endif /* SP_RUNTIME_H */
|