scout 5.1.2 → 5.1.3

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.
Files changed (41) hide show
  1. data/CHANGELOG +5 -0
  2. data/lib/scout.rb +1 -1
  3. data/lib/scout/server.rb +4 -1
  4. data/vendor/json_pure/CHANGES +43 -0
  5. data/vendor/json_pure/{RUBY → COPYING} +1 -1
  6. data/vendor/json_pure/GPL +7 -7
  7. data/vendor/json_pure/README +319 -39
  8. data/vendor/json_pure/Rakefile +69 -47
  9. data/vendor/json_pure/VERSION +1 -1
  10. data/vendor/json_pure/benchmarks/generator2_benchmark.rb +222 -0
  11. data/vendor/json_pure/benchmarks/generator_benchmark.rb +64 -5
  12. data/vendor/json_pure/benchmarks/ohai.json +1216 -0
  13. data/vendor/json_pure/benchmarks/ohai.ruby +1 -0
  14. data/vendor/json_pure/benchmarks/parser2_benchmark.rb +251 -0
  15. data/vendor/json_pure/benchmarks/parser_benchmark.rb +67 -5
  16. data/vendor/json_pure/ext/json/ext/generator/extconf.rb +9 -4
  17. data/vendor/json_pure/ext/json/ext/generator/generator.c +831 -409
  18. data/vendor/json_pure/ext/json/ext/generator/generator.h +170 -0
  19. data/vendor/json_pure/ext/json/ext/parser/extconf.rb +8 -4
  20. data/vendor/json_pure/ext/json/ext/parser/parser.c +292 -186
  21. data/vendor/json_pure/ext/json/ext/parser/parser.h +71 -0
  22. data/vendor/json_pure/ext/json/ext/parser/parser.rl +218 -112
  23. data/vendor/json_pure/lib/json/add/core.rb +20 -7
  24. data/vendor/json_pure/lib/json/add/rails.rb +2 -2
  25. data/vendor/json_pure/lib/json/common.rb +85 -42
  26. data/vendor/json_pure/lib/json/pure.rb +3 -3
  27. data/vendor/json_pure/lib/json/pure/generator.rb +112 -90
  28. data/vendor/json_pure/lib/json/pure/parser.rb +42 -4
  29. data/vendor/json_pure/lib/json/version.rb +1 -1
  30. data/vendor/json_pure/tests/test_json.rb +46 -18
  31. data/vendor/json_pure/tests/test_json_addition.rb +4 -6
  32. data/vendor/json_pure/tests/test_json_encoding.rb +68 -0
  33. data/vendor/json_pure/tests/test_json_generate.rb +30 -14
  34. data/vendor/json_pure/tests/test_json_rails.rb +5 -7
  35. data/vendor/json_pure/tests/test_json_unicode.rb +20 -6
  36. metadata +26 -15
  37. data/vendor/json_pure/doc-templates/main.txt +0 -283
  38. data/vendor/json_pure/ext/json/ext/generator/unicode.c +0 -182
  39. data/vendor/json_pure/ext/json/ext/generator/unicode.h +0 -53
  40. data/vendor/json_pure/ext/json/ext/parser/unicode.c +0 -154
  41. data/vendor/json_pure/ext/json/ext/parser/unicode.h +0 -58
@@ -0,0 +1 @@
1
+ {"kernel"=>{"name"=>"Darwin", "machine"=>"i386", "modules"=>{"com.apple.driver.AppleAPIC"=>{"size"=>12288, "version"=>"1.4", "index"=>"26", "refcount"=>"0"}, "com.apple.driver.AirPort.Atheros"=>{"size"=>593920, "version"=>"318.8.3", "index"=>"88", "refcount"=>"0"}, "com.apple.driver.AppleIntelCPUPowerManagement"=>{"size"=>102400, "version"=>"59.0.1", "index"=>"22", "refcount"=>"0"}, "com.apple.iokit.IOStorageFamily"=>{"size"=>98304, "version"=>"1.5.5", "index"=>"44", "refcount"=>"9"}, "com.apple.iokit.IOATAPIProtocolTransport"=>{"size"=>16384, "version"=>"1.5.2", "index"=>"52", "refcount"=>"0"}, "com.apple.iokit.IOPCIFamily"=>{"size"=>65536, "version"=>"2.5", "index"=>"17", "refcount"=>"18"}, "org.virtualbox.kext.VBoxDrv"=>{"size"=>118784, "version"=>"2.2.0", "index"=>"114", "refcount"=>"3"}, "com.cisco.nke.ipsec"=>{"size"=>454656, "version"=>"2.0.1", "index"=>"111", "refcount"=>"0"}, "com.apple.driver.AppleHPET"=>{"size"=>12288, "version"=>"1.3", "index"=>"33", "refcount"=>"0"}, "com.apple.driver.AppleUSBHub"=>{"size"=>49152, "version"=>"3.2.7", "index"=>"47", "refcount"=>"0"}, "com.apple.iokit.IOFireWireFamily"=>{"size"=>258048, "version"=>"3.4.6", "index"=>"49", "refcount"=>"2"}, "com.apple.driver.AppleUSBComposite"=>{"size"=>16384, "version"=>"3.2.0", "index"=>"60", "refcount"=>"1"}, "com.apple.driver.AppleIntelPIIXATA"=>{"size"=>36864, "version"=>"2.0.0", "index"=>"41", "refcount"=>"0"}, "com.apple.driver.AppleSmartBatteryManager"=>{"size"=>28672, "version"=>"158.6.0", "index"=>"32", "refcount"=>"0"}, "com.apple.filesystems.udf"=>{"size"=>233472, "version"=>"2.0.2", "index"=>"119", "refcount"=>"0"}, "com.apple.iokit.IOSMBusFamily"=>{"size"=>12288, "version"=>"1.1", "index"=>"27", "refcount"=>"2"}, "com.apple.iokit.IOACPIFamily"=>{"size"=>16384, "version"=>"1.2.0", "index"=>"18", "refcount"=>"10"}, "foo.tap"=>{"size"=>24576, "version"=>"1.0", "index"=>"113", "refcount"=>"0"}, "com.vmware.kext.vmx86"=>{"size"=>864256, "version"=>"2.0.4", "index"=>"104", "refcount"=>"0"}, "com.apple.iokit.CHUDUtils"=>{"size"=>28672, "version"=>"200", "index"=>"98", "refcount"=>"0"}, "com.apple.driver.AppleACPIButtons"=>{"size"=>16384, "version"=>"1.2.4", "index"=>"30", "refcount"=>"0"}, "com.apple.driver.AppleFWOHCI"=>{"size"=>139264, "version"=>"3.7.2", "index"=>"50", "refcount"=>"0"}, "com.apple.iokit.IOSCSIArchitectureModelFamily"=>{"size"=>102400, "version"=>"2.0.5", "index"=>"51", "refcount"=>"4"}, "org.virtualbox.kext.VBoxNetAdp"=>{"size"=>8192, "version"=>"2.2.0", "index"=>"117", "refcount"=>"0"}, "com.apple.filesystems.autofs"=>{"size"=>45056, "version"=>"2.0.1", "index"=>"109", "refcount"=>"0"}, "com.vmware.kext.vmnet"=>{"size"=>36864, "version"=>"2.0.4", "index"=>"108", "refcount"=>"0"}, "com.apple.iokit.IOSCSIBlockCommandsDevice"=>{"size"=>90112, "version"=>"2.0.5", "index"=>"57", "refcount"=>"1"}, "com.apple.driver.AppleACPIPCI"=>{"size"=>12288, "version"=>"1.2.4", "index"=>"31", "refcount"=>"0"}, "com.apple.security.seatbelt"=>{"size"=>98304, "version"=>"107.10", "index"=>"25", "refcount"=>"0"}, "com.apple.driver.AppleUpstreamUserClient"=>{"size"=>16384, "version"=>"2.7.2", "index"=>"100", "refcount"=>"0"}, "com.apple.kext.OSvKernDSPLib"=>{"size"=>12288, "version"=>"1.1", "index"=>"79", "refcount"=>"1"}, "com.apple.iokit.IOBDStorageFamily"=>{"size"=>20480, "version"=>"1.5", "index"=>"58", "refcount"=>"1"}, "com.apple.iokit.IOGraphicsFamily"=>{"size"=>118784, "version"=>"1.7.1", "index"=>"70", "refcount"=>"5"}, "com.apple.iokit.IONetworkingFamily"=>{"size"=>90112, "version"=>"1.6.1", "index"=>"82", "refcount"=>"4"}, "com.apple.iokit.IOATAFamily"=>{"size"=>53248, "version"=>"2.0.0", "index"=>"40", "refcount"=>"2"}, "com.apple.iokit.IOUSBHIDDriver"=>{"size"=>20480, "version"=>"3.2.2", "index"=>"63", "refcount"=>"2"}, "org.virtualbox.kext.VBoxUSB"=>{"size"=>28672, "version"=>"2.2.0", "index"=>"115", "refcount"=>"0"}, "com.apple.security.TMSafetyNet"=>{"size"=>12288, "version"=>"3", "index"=>"23", "refcount"=>"0"}, "com.apple.iokit.IONDRVSupport"=>{"size"=>57344, "version"=>"1.7.1", "index"=>"71", "refcount"=>"3"}, "com.apple.BootCache"=>{"size"=>20480, "version"=>"30.3", "index"=>"20", "refcount"=>"0"}, "com.vmware.kext.vmioplug"=>{"size"=>24576, "version"=>"2.0.4", "index"=>"107", "refcount"=>"0"}, "com.apple.iokit.IOUSBUserClient"=>{"size"=>8192, "version"=>"3.2.4", "index"=>"46", "refcount"=>"1"}, "com.apple.iokit.IOSCSIMultimediaCommandsDevice"=>{"size"=>90112, "version"=>"2.0.5", "index"=>"59", "refcount"=>"0"}, "com.apple.driver.AppleIRController"=>{"size"=>20480, "version"=>"110", "index"=>"78", "refcount"=>"0"}, "com.apple.driver.AudioIPCDriver"=>{"size"=>16384, "version"=>"1.0.5", "index"=>"81", "refcount"=>"0"}, "com.apple.driver.AppleLPC"=>{"size"=>12288, "version"=>"1.2.11", "index"=>"73", "refcount"=>"0"}, "org.virtualbox.kext.VBoxNetFlt"=>{"size"=>16384, "version"=>"2.2.0", "index"=>"116", "refcount"=>"0"}, "com.apple.iokit.CHUDKernLib"=>{"size"=>20480, "version"=>"196", "index"=>"93", "refcount"=>"2"}, "com.apple.iokit.CHUDProf"=>{"size"=>49152, "version"=>"207", "index"=>"97", "refcount"=>"0"}, "com.apple.NVDAResman"=>{"size"=>2478080, "version"=>"5.3.6", "index"=>"90", "refcount"=>"2"}, "com.apple.driver.AppleACPIEC"=>{"size"=>20480, "version"=>"1.2.4", "index"=>"28", "refcount"=>"0"}, "foo.tun"=>{"size"=>24576, "version"=>"1.0", "index"=>"118", "refcount"=>"0"}, "com.apple.iokit.IOSerialFamily"=>{"size"=>36864, "version"=>"9.3", "index"=>"102", "refcount"=>"1"}, "com.apple.GeForce"=>{"size"=>622592, "version"=>"5.3.6", "index"=>"96", "refcount"=>"0"}, "com.apple.iokit.IOCDStorageFamily"=>{"size"=>32768, "version"=>"1.5", "index"=>"55", "refcount"=>"3"}, "com.apple.driver.AppleUSBEHCI"=>{"size"=>73728, "version"=>"3.2.5", "index"=>"39", "refcount"=>"0"}, "com.apple.nvidia.nv50hal"=>{"size"=>2445312, "version"=>"5.3.6", "index"=>"91", "refcount"=>"0"}, "com.apple.driver.AppleSMBIOS"=>{"size"=>16384, "version"=>"1.1.1", "index"=>"29", "refcount"=>"0"}, "com.apple.driver.AppleBacklight"=>{"size"=>16384, "version"=>"1.4.4", "index"=>"72", "refcount"=>"0"}, "com.apple.driver.AppleACPIPlatform"=>{"size"=>253952, "version"=>"1.2.4", "index"=>"19", "refcount"=>"3"}, "com.apple.iokit.SCSITaskUserClient"=>{"size"=>24576, "version"=>"2.0.5", "index"=>"54", "refcount"=>"0"}, "com.apple.iokit.IOHIDFamily"=>{"size"=>233472, "version"=>"1.5.3", "index"=>"21", "refcount"=>"7"}, "com.apple.driver.DiskImages"=>{"size"=>65536, "version"=>"195.2.2", "index"=>"101", "refcount"=>"0"}, "com.apple.iokit.IODVDStorageFamily"=>{"size"=>24576, "version"=>"1.5", "index"=>"56", "refcount"=>"2"}, "com.apple.iokit.IOFireWireIP"=>{"size"=>36864, "version"=>"1.7.6", "index"=>"83", "refcount"=>"0"}, "com.apple.driver.AppleRTC"=>{"size"=>20480, "version"=>"1.2.3", "index"=>"34", "refcount"=>"0"}, "com.apple.driver.XsanFilter"=>{"size"=>20480, "version"=>"2.7.91", "index"=>"53", "refcount"=>"0"}, "com.apple.driver.AppleEFIRuntime"=>{"size"=>12288, "version"=>"1.2.0", "index"=>"35", "refcount"=>"1"}, "com.apple.iokit.IOAHCIBlockStorage"=>{"size"=>69632, "version"=>"1.2.0", "index"=>"48", "refcount"=>"0"}, "com.apple.nke.applicationfirewall"=>{"size"=>32768, "version"=>"1.0.77", "index"=>"24", "refcount"=>"0"}, "com.apple.iokit.IO80211Family"=>{"size"=>126976, "version"=>"215.1", "index"=>"87", "refcount"=>"1"}, "com.vmware.kext.vmci"=>{"size"=>45056, "version"=>"2.0.4", "index"=>"106", "refcount"=>"0"}, "com.apple.iokit.IOAHCIFamily"=>{"size"=>24576, "version"=>"1.5.0", "index"=>"42", "refcount"=>"2"}, "com.apple.driver.AppleUSBUHCI"=>{"size"=>57344, "version"=>"3.2.5", "index"=>"38", "refcount"=>"0"}, "com.apple.driver.AppleUSBMergeNub"=>{"size"=>12288, "version"=>"3.2.4", "index"=>"61", "refcount"=>"0"}, "com.apple.iokit.IOUSBFamily"=>{"size"=>167936, "version"=>"3.2.7", "index"=>"37", "refcount"=>"13"}, "com.apple.driver.AppleEFINVRAM"=>{"size"=>24576, "version"=>"1.2.0", "index"=>"36", "refcount"=>"0"}, "com.apple.driver.AppleAHCIPort"=>{"size"=>53248, "version"=>"1.5.2", "index"=>"43", "refcount"=>"0"}}, "os"=>"Darwin", "version"=>"Darwin Kernel Version 9.6.0: Mon Nov 24 17:37:00 PST 2008; root:xnu-1228.9.59~1/RELEASE_I386", "release"=>"9.6.0"}, "command"=>{"ps"=>"ps -ef"}, "platform"=>"mac_os_x", "platform_version"=>"10.5.6", "keys"=>{"ssh"=>{"host_dsa_public"=>"private", "host_rsa_public"=>"private"}}, "ipaddress"=>"192.168.88.1", "fqdn"=>"local.local", "network"=>{"settings"=>{"net.inet6.ip6.forwarding"=>"0", "net.inet.ip.dummynet.debug"=>"0", "net.inet.ip.rtexpire"=>"10", "net.inet6.ipsec6.esp_trans_deflev"=>"1", "net.inet.tcp.tcbhashsize"=>"4096", "net.key.esp_auth"=>"0", "net.inet6.ip6.hlim"=>"64", "net.inet.ip.fw.dyn_fin_lifetime"=>"1", "net.inet.ip.fw.dyn_udp_lifetime"=>"10", "net.inet.icmp.bmcastecho"=>"1", "net.athbgscan"=>"1 1", "net.inet.tcp.reass.maxsegments"=>"2048", "net.athforceBias"=>"2 2", "net.inet6.ip6.auto_flowlabel"=>"1", "net.inet6.ip6.rtmaxcache"=>"128", "net.inet.tcp.sendspace"=>"131072", "net.inet.tcp.keepinit"=>"75000", "net.inet.ip.dummynet.max_chain_len"=>"16", "net.inet.tcp.rfc1644"=>"0", "net.inet.ip.fw.curr_dyn_buckets"=>"256", "net.inet.ip.dummynet.ready_heap"=>"0", "net.inet.ip.portrange.first"=>"49152", "net.inet.tcp.background_io_trigger"=>"5", "net.link.ether.inet.host_down_time"=>"20", "net.inet6.ipsec6.def_policy"=>"1", "net.inet6.ipsec6.ecn"=>"0", "net.inet.ip.fastforwarding"=>"0", "net.inet6.ip6.v6only"=>"0", "net.inet.tcp.sack"=>"1", "net.inet6.ip6.rtexpire"=>"3600", "net.link.ether.inet.proxyall"=>"0", "net.athaddbaignore"=>"0 0", "net.inet6.ip6.keepfaith"=>"0", "net.key.spi_trycnt"=>"1000", "net.link.ether.inet.prune_intvl"=>"300", "net.inet.tcp.ecn_initiate_out"=>"0", "net.inet.ip.fw.dyn_rst_lifetime"=>"1", "net.local.stream.sendspace"=>"8192", "net.inet.tcp.socket_unlocked_on_output"=>"1", "net.inet.ip.fw.verbose_limit"=>"0", "net.local.dgram.recvspace"=>"4096", "net.inet.ipsec.debug"=>"0", "net.link.ether.inet.log_arp_warnings"=>"0", "net.inet.tcp.ecn_negotiate_in"=>"0", "net.inet.tcp.rfc3465"=>"1", "net.inet.tcp.icmp_may_rst"=>"1", "net.link.ether.inet.sendllconflict"=>"0", "net.inet.ipsec.ah_offsetmask"=>"0", "net.key.blockacq_count"=>"10", "net.inet.tcp.delayed_ack"=>"3", "net.inet.ip.fw.verbose"=>"2", "net.inet.ip.fw.dyn_count"=>"0", "net.inet.tcp.slowlink_wsize"=>"8192", "net.inet6.ip6.fw.enable"=>"1", "net.inet.ip.portrange.hilast"=>"65535", "net.inet.icmp.maskrepl"=>"0", "net.link.ether.inet.apple_hwcksum_rx"=>"1", "net.inet.tcp.drop_synfin"=>"1", "net.key.spi_maxval"=>"268435455", "net.inet.ipsec.ecn"=>"0", "net.inet.ip.fw.dyn_keepalive"=>"1", "net.key.int_random"=>"60", "net.key.debug"=>"0", "net.inet.ip.dummynet.curr_time"=>"0", "net.inet.udp.blackhole"=>"0", "net.athaggrqmin"=>"1 1", "net.inet.ip.fw.dyn_syn_lifetime"=>"20", "net.inet.tcp.keepidle"=>"7200000", "net.inet6.ip6.tempvltime"=>"604800", "net.inet.tcp.recvspace"=>"358400", "net.inet.udp.maxdgram"=>"9216", "net.inet.tcp.keepintvl"=>"75000", "net.inet.ip.maxchainsent"=>"0", "net.athppmenable"=>"1 1", "net.inet.ipsec.esp_net_deflev"=>"1", "net.inet6.icmp6.nd6_useloopback"=>"1", "net.inet.tcp.slowstart_flightsize"=>"1", "net.inet.ip.fw.debug"=>"0", "net.inet.ip.linklocal.in.allowbadttl"=>"1", "net.key.spi_minval"=>"256", "net.inet.ip.forwarding"=>"0", "net.inet.tcp.v6mssdflt"=>"1024", "net.key.larval_lifetime"=>"30", "net.inet6.ip6.fw.verbose_limit"=>"0", "net.inet.ip.dummynet.red_lookup_depth"=>"256", "net.inet.tcp.pcbcount"=>"36", "net.inet.ip.fw.dyn_ack_lifetime"=>"300", "net.athCCAThreshold"=>"28 28", "net.inet.ip.portrange.lowlast"=>"600", "net.link.ether.inet.useloopback"=>"1", "net.athqdepth"=>"0 0", "net.inet.ip.ttl"=>"64", "net.inet.ip.rtmaxcache"=>"128", "net.inet.ipsec.bypass"=>"0", "net.inet6.icmp6.nd6_debug"=>"0", "net.inet.ip.use_route_genid"=>"1", "net.inet6.icmp6.rediraccept"=>"1", "net.inet.ip.fw.static_count"=>"1", "net.inet6.ip6.fw.debug"=>"0", "net.inet.udp.pcbcount"=>"104", "net.inet.ipsec.esp_randpad"=>"-1", "net.inet6.icmp6.nd6_maxnudhint"=>"0", "net.inet.tcp.always_keepalive"=>"0", "net.inet.udp.checksum"=>"1", "net.link.ether.inet.keep_announcements"=>"1", "net.athfixedDropThresh"=>"150 150", "net.inet6.ip6.kame_version"=>"20010528/apple-darwin", "net.inet.ip.fw.dyn_max"=>"4096", "net.inet.udp.log_in_vain"=>"0", "net.inet6.icmp6.nd6_mmaxtries"=>"3", "net.inet.ip.rtminexpire"=>"10", "net.inet.ip.fw.dyn_buckets"=>"256", "net.inet6.ip6.accept_rtadv"=>"0", "net.inet6.ip6.rr_prune"=>"5", "net.key.ah_keymin"=>"128", "net.inet.ip.redirect"=>"1", "net.inet.tcp.sack_globalmaxholes"=>"65536", "net.inet.ip.keepfaith"=>"0", "net.inet.ip.dummynet.expire"=>"1", "net.inet.ip.gifttl"=>"30", "net.inet.ip.portrange.last"=>"65535", "net.inet.ipsec.ah_net_deflev"=>"1", "net.inet6.icmp6.nd6_delay"=>"5", "net.inet.tcp.packetchain"=>"50", "net.inet6.ip6.hdrnestlimit"=>"50", "net.inet.tcp.newreno"=>"0", "net.inet6.ip6.dad_count"=>"1", "net.inet6.ip6.auto_linklocal"=>"1", "net.inet6.ip6.temppltime"=>"86400", "net.inet.tcp.strict_rfc1948"=>"0", "net.inet.ip.dummynet.red_max_pkt_size"=>"1500", "net.inet.ip.maxfrags"=>"2048", "net.inet.tcp.log_in_vain"=>"0", "net.athdupie"=>"1 1", "net.inet.tcp.rfc1323"=>"1", "net.inet.ip.subnets_are_local"=>"0", "net.inet.ip.dummynet.search_steps"=>"0", "net.inet.icmp.icmplim"=>"250", "net.link.ether.inet.apple_hwcksum_tx"=>"1", "net.inet6.icmp6.redirtimeout"=>"600", "net.inet.ipsec.ah_cleartos"=>"1", "net.inet6.ip6.log_interval"=>"5", "net.link.ether.inet.max_age"=>"1200", "net.inet.ip.fw.enable"=>"1", "net.inet6.ip6.redirect"=>"1", "net.athaggrfmax"=>"28 28", "net.inet.ip.maxfragsperpacket"=>"128", "net.inet6.ip6.use_deprecated"=>"1", "net.link.generic.system.dlil_input_sanity_check"=>"0", "net.inet.tcp.sack_globalholes"=>"0", "net.inet.tcp.reass.cursegments"=>"0", "net.inet6.icmp6.nodeinfo"=>"3", "net.local.inflight"=>"0", "net.inet.ip.dummynet.hash_size"=>"64", "net.inet.ip.dummynet.red_avg_pkt_size"=>"512", "net.inet.ipsec.dfbit"=>"0", "net.inet.tcp.reass.overflows"=>"0", "net.inet.tcp.rexmt_thresh"=>"2", "net.inet6.ip6.maxfrags"=>"8192", "net.inet6.ip6.rtminexpire"=>"10", "net.inet6.ipsec6.esp_net_deflev"=>"1", "net.inet.tcp.blackhole"=>"0", "net.key.esp_keymin"=>"256", "net.inet.ip.check_interface"=>"0", "net.inet.tcp.minmssoverload"=>"0", "net.link.ether.inet.maxtries"=>"5", "net.inet.tcp.do_tcpdrain"=>"0", "net.inet.ipsec.esp_port"=>"4500", "net.inet6.ipsec6.ah_net_deflev"=>"1", "net.inet.ip.dummynet.extract_heap"=>"0", "net.inet.tcp.path_mtu_discovery"=>"1", "net.inet.ip.intr_queue_maxlen"=>"50", "net.inet.ipsec.def_policy"=>"1", "net.inet.ip.fw.autoinc_step"=>"100", "net.inet.ip.accept_sourceroute"=>"0", "net.inet.raw.maxdgram"=>"8192", "net.inet.ip.maxfragpackets"=>"1024", "net.inet.ip.fw.one_pass"=>"0", "net.appletalk.routermix"=>"2000", "net.inet.tcp.tcp_lq_overflow"=>"1", "net.link.generic.system.ifcount"=>"9", "net.link.ether.inet.send_conflicting_probes"=>"1", "net.inet.tcp.background_io_enabled"=>"1", "net.inet6.ipsec6.debug"=>"0", "net.inet.tcp.win_scale_factor"=>"3", "net.key.natt_keepalive_interval"=>"20", "net.inet.tcp.msl"=>"15000", "net.inet.ip.portrange.hifirst"=>"49152", "net.inet.ipsec.ah_trans_deflev"=>"1", "net.inet.tcp.rtt_min"=>"1", "net.inet6.ip6.defmcasthlim"=>"1", "net.inet6.icmp6.nd6_prune"=>"1", "net.inet6.ip6.fw.verbose"=>"0", "net.inet.ip.portrange.lowfirst"=>"1023", "net.inet.tcp.maxseg_unacked"=>"8", "net.local.dgram.maxdgram"=>"2048", "net.key.blockacq_lifetime"=>"20", "net.inet.tcp.sack_maxholes"=>"128", "net.inet6.ip6.maxfragpackets"=>"1024", "net.inet6.ip6.use_tempaddr"=>"0", "net.athpowermode"=>"0 0", "net.inet.udp.recvspace"=>"73728", "net.inet.tcp.isn_reseed_interval"=>"0", "net.inet.tcp.local_slowstart_flightsize"=>"8", "net.inet.ip.dummynet.searches"=>"0", "net.inet.ip.intr_queue_drops"=>"0", "net.link.generic.system.multi_threaded_input"=>"1", "net.inet.raw.recvspace"=>"8192", "net.inet.ipsec.esp_trans_deflev"=>"1", "net.key.prefered_oldsa"=>"0", "net.local.stream.recvspace"=>"8192", "net.inet.tcp.sockthreshold"=>"64", "net.inet6.icmp6.nd6_umaxtries"=>"3", "net.pstimeout"=>"20 20", "net.inet.ip.sourceroute"=>"0", "net.inet.ip.fw.dyn_short_lifetime"=>"5", "net.inet.tcp.minmss"=>"216", "net.inet6.ip6.gifhlim"=>"0", "net.athvendorie"=>"1 1", "net.inet.ip.check_route_selfref"=>"1", "net.inet.icmp.log_redirect"=>"0", "net.inet6.icmp6.errppslimit"=>"100", "net.inet.tcp.mssdflt"=>"512", "net.inet.icmp.drop_redirect"=>"0", "net.inet6.ipsec6.esp_randpad"=>"-1", "net.inet6.ipsec6.ah_trans_deflev"=>"1", "net.inet.ip.random_id"=>"1", "net.inet.icmp.timestamp"=>"0"}, "interfaces"=>{"stf0"=>{"flags"=>[], "number"=>"0", "mtu"=>"1280", "type"=>"stf", "encapsulation"=>"6to4"}, "vmnet1"=>{"flags"=>["UP", "BROADCAST", "SMART", "RUNNING", "SIMPLEX", "MULTICAST"], "number"=>"1", "addresses"=>[{"broadcast"=>"192.168.88.255", "netmask"=>"255.255.255.0", "family"=>"inet", "address"=>"192.168.88.1"}, {"family"=>"lladdr", "address"=>"private"}], "mtu"=>"1500", "type"=>"vmnet", "encapsulation"=>"Ethernet"}, "vboxnet0"=>{"flags"=>["BROADCAST", "RUNNING", "SIMPLEX", "MULTICAST"], "number"=>"0", "addresses"=>[{"family"=>"lladdr", "address"=>"private"}], "mtu"=>"1500", "type"=>"vboxnet", "encapsulation"=>"Ethernet"}, "lo0"=>{"flags"=>["UP", "LOOPBACK", "RUNNING", "MULTICAST"], "number"=>"0", "addresses"=>[{"scope"=>"Link", "prefixlen"=>"64", "family"=>"inet6", "address"=>"fe80::1"}, {"netmask"=>"255.0.0.0", "family"=>"inet", "address"=>"127.0.0.1"}, {"scope"=>"Node", "prefixlen"=>"128", "family"=>"inet6", "address"=>"::1"}, {"scope"=>"Node", "prefixlen"=>"128", "family"=>"inet6", "address"=>"private"}], "mtu"=>"16384", "type"=>"lo", "encapsulation"=>"Loopback"}, "vboxn"=>{"counters"=>{"tx"=>{"packets"=>"0", "bytes"=>"0", "compressed"=>0, "collisions"=>"0", "carrier"=>0, "errors"=>"0", "drop"=>0, "overrun"=>0}, "rx"=>{"packets"=>"0", "bytes"=>"0", "compressed"=>0, "errors"=>"0", "drop"=>0, "overrun"=>0, "multicast"=>0, "frame"=>0}}}, "gif0"=>{"flags"=>["POINTOPOINT", "MULTICAST"], "number"=>"0", "mtu"=>"1280", "type"=>"gif", "encapsulation"=>"IPIP"}, "vmnet"=>{"counters"=>{"tx"=>{"packets"=>"0", "bytes"=>"0", "compressed"=>0, "collisions"=>"0", "carrier"=>0, "errors"=>"0", "drop"=>0, "overrun"=>0}, "rx"=>{"packets"=>"0", "bytes"=>"0", "compressed"=>0, "errors"=>"0", "drop"=>0, "overrun"=>0, "multicast"=>0, "frame"=>0}}}, "en0"=>{"flags"=>["UP", "BROADCAST", "SMART", "RUNNING", "SIMPLEX", "MULTICAST"], "status"=>"inactive", "number"=>"0", "addresses"=>[{"family"=>"lladdr", "address"=>"private"}], "mtu"=>"1500", "type"=>"en", "media"=>{"supported"=>[{"autoselect"=>{"options"=>[]}}, {"10baseT/UTP"=>{"options"=>["half-duplex"]}}, {"10baseT/UTP"=>{"options"=>["full-duplex"]}}, {"10baseT/UTP"=>{"options"=>["full-duplex", "hw-loopback"]}}, {"10baseT/UTP"=>{"options"=>["full-duplex", "flow-control"]}}, {"100baseTX"=>{"options"=>["half-duplex"]}}, {"100baseTX"=>{"options"=>["full-duplex"]}}, {"100baseTX"=>{"options"=>["full-duplex", "hw-loopback"]}}, {"100baseTX"=>{"options"=>["full-duplex", "flow-control"]}}, {"1000baseT"=>{"options"=>["full-duplex"]}}, {"1000baseT"=>{"options"=>["full-duplex", "hw-loopback"]}}, {"1000baseT"=>{"options"=>["full-duplex", "flow-control"]}}, {"none"=>{"options"=>[]}}], "selected"=>[{"autoselect"=>{"options"=>[]}}]}, "counters"=>{"tx"=>{"packets"=>"0", "bytes"=>"342", "compressed"=>0, "collisions"=>"0", "carrier"=>0, "errors"=>"0", "drop"=>0, "overrun"=>0}, "rx"=>{"packets"=>"0", "bytes"=>"0", "compressed"=>0, "errors"=>"0", "drop"=>0, "overrun"=>0, "multicast"=>0, "frame"=>0}}, "encapsulation"=>"Ethernet"}, "vmnet8"=>{"flags"=>["UP", "BROADCAST", "SMART", "RUNNING", "SIMPLEX", "MULTICAST"], "number"=>"8", "addresses"=>[{"broadcast"=>"192.168.237.255", "netmask"=>"255.255.255.0", "family"=>"inet", "address"=>"192.168.237.1"}, {"family"=>"lladdr", "address"=>"private"}], "mtu"=>"1500", "type"=>"vmnet", "encapsulation"=>"Ethernet"}, "en1"=>{"flags"=>["UP", "BROADCAST", "SMART", "RUNNING", "SIMPLEX", "MULTICAST"], "status"=>"active", "number"=>"1", "addresses"=>[{"scope"=>"Link", "prefixlen"=>"64", "family"=>"inet6", "address"=>"private"}, {"broadcast"=>"192.168.1.255", "netmask"=>"255.255.255.0", "family"=>"inet", "address"=>"192.168.1.4"}, {"family"=>"lladdr", "address"=>"private"}], "mtu"=>"1500", "type"=>"en", "media"=>{"supported"=>[{"autoselect"=>{"options"=>[]}}], "selected"=>[{"autoselect"=>{"options"=>[]}}]}, "counters"=>{"tx"=>{"packets"=>"7041789", "bytes"=>"449206298", "compressed"=>0, "collisions"=>"0", "carrier"=>0, "errors"=>"95", "drop"=>0, "overrun"=>0}, "rx"=>{"packets"=>"19966002", "bytes"=>"13673879120", "compressed"=>0, "errors"=>"1655893", "drop"=>0, "overrun"=>0, "multicast"=>0, "frame"=>0}}, "encapsulation"=>"Ethernet", "arp"=>{"192.168.1.7"=>"private"}}, "fw0"=>{"flags"=>["UP", "BROADCAST", "SMART", "RUNNING", "SIMPLEX", "MULTICAST"], "status"=>"inactive", "number"=>"0", "addresses"=>[{"family"=>"lladdr", "address"=>"private"}], "mtu"=>"4078", "type"=>"fw", "media"=>{"supported"=>[{"autoselect"=>{"options"=>["full-duplex"]}}], "selected"=>[{"autoselect"=>{"options"=>["full-duplex"]}}]}, "counters"=>{"tx"=>{"packets"=>"0", "bytes"=>"346", "compressed"=>0, "collisions"=>"0", "carrier"=>0, "errors"=>"0", "drop"=>0, "overrun"=>0}, "rx"=>{"packets"=>"0", "bytes"=>"0", "compressed"=>0, "errors"=>"0", "drop"=>0, "overrun"=>0, "multicast"=>0, "frame"=>0}}, "encapsulation"=>"1394"}}}, "os"=>"darwin", "domain"=>"local", "ohai_time"=>1240624355.08575, "platform_build"=>"9G55", "os_version"=>"9.6.0", "hostname"=>"local", "languages"=>{"ruby"=>{"target_os"=>"darwin9.0", "platform"=>"universal-darwin9.0", "host_vendor"=>"apple", "target_cpu"=>"i686", "target_vendor"=>"apple", "host_os"=>"darwin9.0", "version"=>"1.8.6", "host_cpu"=>"i686", "host"=>"i686-apple-darwin9.0", "release_date"=>"2008-03-03", "target"=>"i686-apple-darwin9.0"}}, "macaddress"=>"private"}
@@ -0,0 +1,251 @@
1
+ #!/usr/bin/env ruby
2
+ # CODING: UTF-8
3
+
4
+ require 'rbconfig'
5
+ RUBY_PATH=File.join(Config::CONFIG['bindir'], Config::CONFIG['ruby_install_name'])
6
+ RAKE_PATH=File.join(Config::CONFIG['bindir'], 'rake')
7
+ require 'bullshit'
8
+ case ARGV.first
9
+ when 'ext'
10
+ require 'json/ext'
11
+ when 'pure'
12
+ require 'json/pure'
13
+ when 'yaml'
14
+ require 'yaml'
15
+ require 'json/pure'
16
+ when 'rails'
17
+ require 'active_support'
18
+ require 'json/pure'
19
+ when 'yajl'
20
+ require 'yajl'
21
+ require 'json/pure'
22
+ else
23
+ require 'json/pure'
24
+ end
25
+
26
+ module Parser2BenchmarkCommon
27
+ include JSON
28
+
29
+ def setup
30
+ @big = @json = File.read(File.join(File.dirname(__FILE__), 'ohai.json'))
31
+ end
32
+
33
+ def generic_reset_method
34
+ @result == @big or raise "not equal"
35
+ end
36
+ end
37
+
38
+ class Parser2BenchmarkExt < Bullshit::RepeatCase
39
+ include Parser2BenchmarkCommon
40
+
41
+ warmup yes
42
+ iterations 2000
43
+
44
+ truncate_data do
45
+ enabled false
46
+ alpha_level 0.05
47
+ window_size 50
48
+ slope_angle 0.1
49
+ end
50
+
51
+ autocorrelation do
52
+ alpha_level 0.05
53
+ max_lags 50
54
+ file yes
55
+ end
56
+
57
+ output_dir File.join(File.dirname(__FILE__), 'data')
58
+ output_filename benchmark_name + '.log'
59
+ data_file yes
60
+ histogram yes
61
+
62
+ def benchmark_parser
63
+ @result = JSON.parse(@json)
64
+ end
65
+
66
+ alias reset_parser generic_reset_method
67
+
68
+ def benchmark_parser_symbolic
69
+ @result = JSON.parse(@json, :symbolize_names => true)
70
+ end
71
+
72
+ alias reset_parser_symbolc generic_reset_method
73
+ end
74
+
75
+ class Parser2BenchmarkPure < Bullshit::RepeatCase
76
+ include Parser2BenchmarkCommon
77
+
78
+ warmup yes
79
+ iterations 400
80
+
81
+ truncate_data do
82
+ enabled false
83
+ alpha_level 0.05
84
+ window_size 50
85
+ slope_angle 0.1
86
+ end
87
+
88
+ autocorrelation do
89
+ alpha_level 0.05
90
+ max_lags 50
91
+ file yes
92
+ end
93
+
94
+ output_dir File.join(File.dirname(__FILE__), 'data')
95
+ output_filename benchmark_name + '.log'
96
+ data_file yes
97
+ histogram yes
98
+
99
+ def benchmark_parser
100
+ @result = JSON.parse(@json)
101
+ end
102
+
103
+ alias reset_parser generic_reset_method
104
+
105
+ def benchmark_parser_symbolic
106
+ @result = JSON.parse(@json, :symbolize_names => true)
107
+ end
108
+
109
+ alias reset_parser_symbolc generic_reset_method
110
+ end
111
+
112
+ class Parser2BenchmarkYAML < Bullshit::RepeatCase
113
+ warmup yes
114
+ iterations 400
115
+
116
+ truncate_data do
117
+ enabled false
118
+ alpha_level 0.05
119
+ window_size 50
120
+ slope_angle 0.1
121
+ end
122
+
123
+ autocorrelation do
124
+ alpha_level 0.05
125
+ max_lags 50
126
+ file yes
127
+ end
128
+
129
+ output_dir File.join(File.dirname(__FILE__), 'data')
130
+ output_filename benchmark_name + '.log'
131
+ data_file yes
132
+ histogram yes
133
+
134
+ def setup
135
+ @big = @json = File.read(File.join(File.dirname(__FILE__), 'ohai.json'))
136
+ end
137
+
138
+ def benchmark_parser
139
+ @result = YAML.load(@json)
140
+ end
141
+
142
+ def generic_reset_method
143
+ @result == @big or raise "not equal"
144
+ end
145
+ end
146
+
147
+ class Parser2BenchmarkRails < Bullshit::RepeatCase
148
+ warmup yes
149
+ iterations 400
150
+
151
+ truncate_data do
152
+ alpha_level 0.05
153
+ window_size 50
154
+ slope_angle 0.1
155
+ end
156
+
157
+ autocorrelation do
158
+ alpha_level 0.05
159
+ max_lags 50
160
+ file yes
161
+ end
162
+
163
+ output_dir File.join(File.dirname(__FILE__), 'data')
164
+ output_filename benchmark_name + '.log'
165
+ data_file yes
166
+ histogram yes
167
+
168
+ def setup
169
+ a = [ nil, false, true, "fÖß\nÄr", [ "n€st€d", true ], { "fooß" => "bär", "qu\r\nux" => true } ]
170
+ @big = a * 100
171
+ @json = JSON.generate(@big)
172
+ end
173
+
174
+ def benchmark_parser
175
+ @result = ActiveSupport::JSON.decode(@json)
176
+ end
177
+
178
+ def generic_reset_method
179
+ @result == @big or raise "not equal"
180
+ end
181
+ end
182
+
183
+ class Parser2BenchmarkYajl < Bullshit::RepeatCase
184
+ warmup yes
185
+ iterations 2000
186
+
187
+ truncate_data do
188
+ alpha_level 0.05
189
+ window_size 50
190
+ slope_angle 0.1
191
+ end
192
+
193
+ autocorrelation do
194
+ alpha_level 0.05
195
+ max_lags 50
196
+ file yes
197
+ end
198
+
199
+ output_dir File.join(File.dirname(__FILE__), 'data')
200
+ output_filename benchmark_name + '.log'
201
+ data_file yes
202
+ histogram yes
203
+
204
+ def setup
205
+ @big = @json = File.read(File.join(File.dirname(__FILE__), 'ohai.json'))
206
+ end
207
+
208
+ def benchmark_parser
209
+ @result = Yajl::Parser.new.parse(@json)
210
+ end
211
+
212
+ def generic_reset_method
213
+ @result == @big or raise "not equal"
214
+ end
215
+ end
216
+
217
+ if $0 == __FILE__
218
+ Bullshit::Case.autorun false
219
+
220
+ case ARGV.first
221
+ when 'ext'
222
+ Parser2BenchmarkExt.run
223
+ when 'pure'
224
+ Parser2BenchmarkPure.run
225
+ when 'yaml'
226
+ Parser2BenchmarkYAML.run
227
+ when 'rails'
228
+ Parser2BenchmarkRails.run
229
+ when 'yajl'
230
+ Parser2BenchmarkYajl.run
231
+ else
232
+ system "#{RAKE_PATH} clean"
233
+ system "#{RUBY_PATH} #$0 yaml"
234
+ system "#{RUBY_PATH} #$0 rails"
235
+ system "#{RUBY_PATH} #$0 pure"
236
+ system "#{RAKE_PATH} compile_ext"
237
+ system "#{RUBY_PATH} #$0 ext"
238
+ system "#{RUBY_PATH} #$0 yajl"
239
+ Bullshit.compare do
240
+ output_filename File.join(File.dirname(__FILE__), 'data', 'Parser2BenchmarkComparison.log')
241
+
242
+ benchmark Parser2BenchmarkExt, :parser, :load => yes
243
+ benchmark Parser2BenchmarkExt, :parser_symbolic, :load => yes
244
+ benchmark Parser2BenchmarkPure, :parser, :load => yes
245
+ benchmark Parser2BenchmarkPure, :parser_symbolic, :load => yes
246
+ benchmark Parser2BenchmarkYAML, :parser, :load => yes
247
+ benchmark Parser2BenchmarkRails, :parser, :load => yes
248
+ benchmark Parser2BenchmarkYajl, :parser, :load => yes
249
+ end
250
+ end
251
+ end
@@ -16,6 +16,9 @@ when 'yaml'
16
16
  when 'rails'
17
17
  require 'active_support'
18
18
  require 'json/pure'
19
+ when 'yajl'
20
+ require 'yajl'
21
+ require 'json/pure'
19
22
  else
20
23
  require 'json/pure'
21
24
  end
@@ -38,9 +41,10 @@ class ParserBenchmarkExt < Bullshit::RepeatCase
38
41
  include ParserBenchmarkCommon
39
42
 
40
43
  warmup yes
41
- iterations 1000
44
+ iterations 2000
42
45
 
43
46
  truncate_data do
47
+ enabled false
44
48
  alpha_level 0.05
45
49
  window_size 50
46
50
  slope_angle 0.1
@@ -62,15 +66,22 @@ class ParserBenchmarkExt < Bullshit::RepeatCase
62
66
  end
63
67
 
64
68
  alias reset_parser generic_reset_method
69
+
70
+ def benchmark_parser_symbolic
71
+ @result = JSON.parse(@json, :symbolize_names => true)
72
+ end
73
+
74
+ alias reset_parser_symbolc generic_reset_method
65
75
  end
66
76
 
67
77
  class ParserBenchmarkPure < Bullshit::RepeatCase
68
78
  include ParserBenchmarkCommon
69
79
 
70
80
  warmup yes
71
- iterations 1000
81
+ iterations 400
72
82
 
73
83
  truncate_data do
84
+ enabled false
74
85
  alpha_level 0.05
75
86
  window_size 50
76
87
  slope_angle 0.1
@@ -92,13 +103,20 @@ class ParserBenchmarkPure < Bullshit::RepeatCase
92
103
  end
93
104
 
94
105
  alias reset_parser generic_reset_method
106
+
107
+ def benchmark_parser_symbolic
108
+ @result = JSON.parse(@json, :symbolize_names => true)
109
+ end
110
+
111
+ alias reset_parser_symbolc generic_reset_method
95
112
  end
96
113
 
97
114
  class ParserBenchmarkYAML < Bullshit::RepeatCase
98
115
  warmup yes
99
- iterations 1000
116
+ iterations 400
100
117
 
101
118
  truncate_data do
119
+ enabled false
102
120
  alpha_level 0.05
103
121
  window_size 50
104
122
  slope_angle 0.1
@@ -132,9 +150,10 @@ end
132
150
 
133
151
  class ParserBenchmarkRails < Bullshit::RepeatCase
134
152
  warmup yes
135
- iterations 1000
153
+ iterations 400
136
154
 
137
155
  truncate_data do
156
+ enabled false
138
157
  alpha_level 0.05
139
158
  window_size 50
140
159
  slope_angle 0.1
@@ -166,6 +185,43 @@ class ParserBenchmarkRails < Bullshit::RepeatCase
166
185
  end
167
186
  end
168
187
 
188
+ class ParserBenchmarkYajl < Bullshit::RepeatCase
189
+ warmup yes
190
+ iterations 2000
191
+
192
+ truncate_data do
193
+ enabled false
194
+ alpha_level 0.05
195
+ window_size 50
196
+ slope_angle 0.1
197
+ end
198
+
199
+ autocorrelation do
200
+ alpha_level 0.05
201
+ max_lags 50
202
+ file yes
203
+ end
204
+
205
+ output_dir File.join(File.dirname(__FILE__), 'data')
206
+ output_filename benchmark_name + '.log'
207
+ data_file yes
208
+ histogram yes
209
+
210
+ def setup
211
+ a = [ nil, false, true, "fÖß\nÄr", [ "n€st€d", true ], { "fooß" => "bär", "qu\r\nux" => true } ]
212
+ @big = a * 100
213
+ @json = JSON.generate(@big)
214
+ end
215
+
216
+ def benchmark_parser
217
+ @result = Yajl::Parser.new.parse(@json)
218
+ end
219
+
220
+ def generic_reset_method
221
+ @result == @big or raise "not equal"
222
+ end
223
+ end
224
+
169
225
  if $0 == __FILE__
170
226
  Bullshit::Case.autorun false
171
227
 
@@ -178,20 +234,26 @@ if $0 == __FILE__
178
234
  ParserBenchmarkYAML.run
179
235
  when 'rails'
180
236
  ParserBenchmarkRails.run
237
+ when 'yajl'
238
+ ParserBenchmarkYajl.run
181
239
  else
182
240
  system "#{RAKE_PATH} clean"
183
241
  system "#{RUBY_PATH} #$0 yaml"
184
242
  system "#{RUBY_PATH} #$0 rails"
185
243
  system "#{RUBY_PATH} #$0 pure"
186
- system "#{RAKE_PATH} compile"
244
+ system "#{RAKE_PATH} compile_ext"
187
245
  system "#{RUBY_PATH} #$0 ext"
246
+ system "#{RUBY_PATH} #$0 yajl"
188
247
  Bullshit.compare do
189
248
  output_filename File.join(File.dirname(__FILE__), 'data', 'ParserBenchmarkComparison.log')
190
249
 
191
250
  benchmark ParserBenchmarkExt, :parser, :load => yes
251
+ benchmark ParserBenchmarkExt, :parser_symbolic, :load => yes
192
252
  benchmark ParserBenchmarkPure, :parser, :load => yes
253
+ benchmark ParserBenchmarkPure, :parser_symbolic, :load => yes
193
254
  benchmark ParserBenchmarkYAML, :parser, :load => yes
194
255
  benchmark ParserBenchmarkRails, :parser, :load => yes
256
+ benchmark ParserBenchmarkYajl, :parser, :load => yes
195
257
  end
196
258
  end
197
259
  end
@@ -1,11 +1,16 @@
1
1
  require 'mkmf'
2
2
  require 'rbconfig'
3
3
 
4
+ unless $CFLAGS.gsub!(/ -O[\dsz]?/, ' -O3')
5
+ $CFLAGS << ' -O3'
6
+ end
4
7
  if CONFIG['CC'] =~ /gcc/
5
- $CFLAGS += ' -Wall'
6
- #$CFLAGS += ' -O0 -ggdb'
8
+ $CFLAGS << ' -Wall'
9
+ #unless $CFLAGS.gsub!(/ -O[\dsz]?/, ' -O0 -ggdb')
10
+ # $CFLAGS << ' -O0 -ggdb'
11
+ #end
7
12
  end
8
13
 
9
- have_header("ruby/st.h") || have_header("st.h")
14
+ have_header("ruby/re.h") || have_header("re.h")
10
15
  have_header("ruby/encoding.h")
11
- create_makefile 'generator'
16
+ create_makefile 'json/ext/generator'
@@ -1,158 +1,424 @@
1
- #include <string.h>
2
- #include "ruby.h"
3
- #if HAVE_RUBY_ST_H
4
- #include "ruby/st.h"
5
- #endif
6
- #if HAVE_ST_H
7
- #include "st.h"
8
- #endif
9
- #include "unicode.h"
10
- #include <math.h>
11
-
12
- #ifndef RHASH_TBL
13
- #define RHASH_TBL(hsh) (RHASH(hsh)->tbl)
14
- #endif
15
-
16
- #ifndef RHASH_SIZE
17
- #define RHASH_SIZE(hsh) (RHASH(hsh)->tbl->num_entries)
18
- #endif
19
-
20
- #ifndef RFLOAT_VALUE
21
- #define RFLOAT_VALUE(val) (RFLOAT(val)->value)
22
- #endif
1
+ #include "generator.h"
23
2
 
24
3
  #ifdef HAVE_RUBY_ENCODING_H
25
- #include "ruby/encoding.h"
26
- #define FORCE_UTF8(obj) rb_enc_associate((obj), rb_utf8_encoding())
27
- #else
28
- #define FORCE_UTF8(obj)
4
+ static VALUE CEncoding_UTF_8;
5
+ static ID i_encoding, i_encode;
29
6
  #endif
30
7
 
31
- #define check_max_nesting(state, depth) do { \
32
- long current_nesting = 1 + depth; \
33
- if (state->max_nesting != 0 && current_nesting > state->max_nesting) \
34
- rb_raise(eNestingError, "nesting of %ld is too deep", current_nesting); \
35
- } while (0);
36
-
37
8
  static VALUE mJSON, mExt, mGenerator, cState, mGeneratorMethods, mObject,
38
9
  mHash, mArray, mInteger, mFloat, mString, mString_Extend,
39
10
  mTrueClass, mFalseClass, mNilClass, eGeneratorError,
40
- eCircularDatastructure, eNestingError;
11
+ eNestingError, CRegexp_MULTILINE, CJSON_SAFE_STATE_PROTOTYPE;
41
12
 
42
13
  static ID i_to_s, i_to_json, i_new, i_indent, i_space, i_space_before,
43
- i_object_nl, i_array_nl, i_check_circular, i_max_nesting,
44
- i_allow_nan, i_pack, i_unpack, i_create_id, i_extend;
45
-
46
- typedef struct JSON_Generator_StateStruct {
47
- VALUE indent;
48
- VALUE space;
49
- VALUE space_before;
50
- VALUE object_nl;
51
- VALUE array_nl;
52
- int check_circular;
53
- VALUE seen;
54
- VALUE memo;
55
- VALUE depth;
56
- long max_nesting;
57
- int flag;
58
- int allow_nan;
59
- } JSON_Generator_State;
60
-
61
- #define GET_STATE(self) \
62
- JSON_Generator_State *state; \
63
- Data_Get_Struct(self, JSON_Generator_State, state);
14
+ i_object_nl, i_array_nl, i_max_nesting, i_allow_nan, i_ascii_only,
15
+ i_pack, i_unpack, i_create_id, i_extend, i_key_p, i_aref, i_send,
16
+ i_respond_to_p, i_match;
64
17
 
65
- /*
66
- * Document-module: JSON::Ext::Generator
67
- *
68
- * This is the JSON generator implemented as a C extension. It can be
69
- * configured to be used by setting
70
- *
71
- * JSON.generator = JSON::Ext::Generator
72
- *
73
- * with the method generator= in JSON.
74
- *
18
+ /*
19
+ * Copyright 2001-2004 Unicode, Inc.
20
+ *
21
+ * Disclaimer
22
+ *
23
+ * This source code is provided as is by Unicode, Inc. No claims are
24
+ * made as to fitness for any particular purpose. No warranties of any
25
+ * kind are expressed or implied. The recipient agrees to determine
26
+ * applicability of information provided. If this file has been
27
+ * purchased on magnetic or optical media from Unicode, Inc., the
28
+ * sole remedy for any claim will be exchange of defective media
29
+ * within 90 days of receipt.
30
+ *
31
+ * Limitations on Rights to Redistribute This Code
32
+ *
33
+ * Unicode, Inc. hereby grants the right to freely use the information
34
+ * supplied in this file in the creation of products supporting the
35
+ * Unicode Standard, and to make copies of this file in any form
36
+ * for internal or external distribution as long as this notice
37
+ * remains attached.
38
+ */
39
+
40
+ /*
41
+ * Index into the table below with the first byte of a UTF-8 sequence to
42
+ * get the number of trailing bytes that are supposed to follow it.
43
+ * Note that *legal* UTF-8 values can't have 4 or 5-bytes. The table is
44
+ * left as-is for anyone who may want to do such conversion, which was
45
+ * allowed in earlier algorithms.
46
+ */
47
+ static const char trailingBytesForUTF8[256] = {
48
+ 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
49
+ 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
50
+ 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
51
+ 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
52
+ 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
53
+ 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
54
+ 1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1, 1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,
55
+ 2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2, 3,3,3,3,3,3,3,3,4,4,4,4,5,5,5,5
56
+ };
57
+
58
+ /*
59
+ * Magic values subtracted from a buffer value during UTF8 conversion.
60
+ * This table contains as many values as there might be trailing bytes
61
+ * in a UTF-8 sequence.
75
62
  */
63
+ static const UTF32 offsetsFromUTF8[6] = { 0x00000000UL, 0x00003080UL, 0x000E2080UL,
64
+ 0x03C82080UL, 0xFA082080UL, 0x82082080UL };
65
+
66
+ /*
67
+ * Utility routine to tell whether a sequence of bytes is legal UTF-8.
68
+ * This must be called with the length pre-determined by the first byte.
69
+ * If not calling this from ConvertUTF8to*, then the length can be set by:
70
+ * length = trailingBytesForUTF8[*source]+1;
71
+ * and the sequence is illegal right away if there aren't that many bytes
72
+ * available.
73
+ * If presented with a length > 4, this returns 0. The Unicode
74
+ * definition of UTF-8 goes up to 4-byte sequences.
75
+ */
76
+ static unsigned char isLegalUTF8(const UTF8 *source, int length)
77
+ {
78
+ UTF8 a;
79
+ const UTF8 *srcptr = source+length;
80
+ switch (length) {
81
+ default: return 0;
82
+ /* Everything else falls through when "1"... */
83
+ case 4: if ((a = (*--srcptr)) < 0x80 || a > 0xBF) return 0;
84
+ case 3: if ((a = (*--srcptr)) < 0x80 || a > 0xBF) return 0;
85
+ case 2: if ((a = (*--srcptr)) > 0xBF) return 0;
86
+
87
+ switch (*source) {
88
+ /* no fall-through in this inner switch */
89
+ case 0xE0: if (a < 0xA0) return 0; break;
90
+ case 0xED: if (a > 0x9F) return 0; break;
91
+ case 0xF0: if (a < 0x90) return 0; break;
92
+ case 0xF4: if (a > 0x8F) return 0; break;
93
+ default: if (a < 0x80) return 0;
94
+ }
95
+
96
+ case 1: if (*source >= 0x80 && *source < 0xC2) return 0;
97
+ }
98
+ if (*source > 0xF4) return 0;
99
+ return 1;
100
+ }
101
+
102
+ /* Escapes the UTF16 character and stores the result in the buffer buf. */
103
+ static void unicode_escape(char *buf, UTF16 character)
104
+ {
105
+ const char *digits = "0123456789abcdef";
76
106
 
77
- static int hash_to_json_state_i(VALUE key, VALUE value, VALUE Vstate)
107
+ buf[2] = digits[character >> 12];
108
+ buf[3] = digits[(character >> 8) & 0xf];
109
+ buf[4] = digits[(character >> 4) & 0xf];
110
+ buf[5] = digits[character & 0xf];
111
+ }
112
+
113
+ /* Escapes the UTF16 character and stores the result in the buffer buf, then
114
+ * the buffer buf іs appended to the FBuffer buffer. */
115
+ static void unicode_escape_to_buffer(FBuffer *buffer, char buf[6], UTF16
116
+ character)
78
117
  {
79
- VALUE json, buf, Vdepth;
80
- GET_STATE(Vstate);
81
- buf = state->memo;
82
- Vdepth = state->depth;
118
+ unicode_escape(buf, character);
119
+ fbuffer_append(buffer, buf, 6);
120
+ }
83
121
 
84
- if (key == Qundef) return ST_CONTINUE;
85
- if (state->flag) {
86
- state->flag = 0;
87
- rb_str_buf_cat2(buf, ",");
88
- if (RSTRING_LEN(state->object_nl)) rb_str_buf_append(buf, state->object_nl);
122
+ /* Converts string to a JSON string in FBuffer buffer, where all but the ASCII
123
+ * and control characters are JSON escaped. */
124
+ static void convert_UTF8_to_JSON_ASCII(FBuffer *buffer, VALUE string)
125
+ {
126
+ const UTF8 *source = (UTF8 *) RSTRING_PTR(string);
127
+ const UTF8 *sourceEnd = source + RSTRING_LEN(string);
128
+ char buf[6] = { '\\', 'u' };
129
+
130
+ while (source < sourceEnd) {
131
+ UTF32 ch = 0;
132
+ unsigned short extraBytesToRead = trailingBytesForUTF8[*source];
133
+ if (source + extraBytesToRead >= sourceEnd) {
134
+ rb_raise(rb_path2class("JSON::GeneratorError"),
135
+ "partial character in source, but hit end");
136
+ }
137
+ if (!isLegalUTF8(source, extraBytesToRead+1)) {
138
+ rb_raise(rb_path2class("JSON::GeneratorError"),
139
+ "source sequence is illegal/malformed utf-8");
140
+ }
141
+ /*
142
+ * The cases all fall through. See "Note A" below.
143
+ */
144
+ switch (extraBytesToRead) {
145
+ case 5: ch += *source++; ch <<= 6; /* remember, illegal UTF-8 */
146
+ case 4: ch += *source++; ch <<= 6; /* remember, illegal UTF-8 */
147
+ case 3: ch += *source++; ch <<= 6;
148
+ case 2: ch += *source++; ch <<= 6;
149
+ case 1: ch += *source++; ch <<= 6;
150
+ case 0: ch += *source++;
151
+ }
152
+ ch -= offsetsFromUTF8[extraBytesToRead];
153
+
154
+ if (ch <= UNI_MAX_BMP) { /* Target is a character <= 0xFFFF */
155
+ /* UTF-16 surrogate values are illegal in UTF-32 */
156
+ if (ch >= UNI_SUR_HIGH_START && ch <= UNI_SUR_LOW_END) {
157
+ #if UNI_STRICT_CONVERSION
158
+ source -= (extraBytesToRead+1); /* return to the illegal value itself */
159
+ rb_raise(rb_path2class("JSON::GeneratorError"),
160
+ "source sequence is illegal/malformed utf-8");
161
+ #else
162
+ unicode_escape_to_buffer(buffer, buf, UNI_REPLACEMENT_CHAR);
163
+ #endif
164
+ } else {
165
+ /* normal case */
166
+ if (ch >= 0x20 && ch <= 0x7f) {
167
+ switch (ch) {
168
+ case '\\':
169
+ fbuffer_append(buffer, "\\\\", 2);
170
+ break;
171
+ case '"':
172
+ fbuffer_append(buffer, "\\\"", 2);
173
+ break;
174
+ default:
175
+ fbuffer_append_char(buffer, (char)ch);
176
+ break;
177
+ }
178
+ } else {
179
+ switch (ch) {
180
+ case '\n':
181
+ fbuffer_append(buffer, "\\n", 2);
182
+ break;
183
+ case '\r':
184
+ fbuffer_append(buffer, "\\r", 2);
185
+ break;
186
+ case '\t':
187
+ fbuffer_append(buffer, "\\t", 2);
188
+ break;
189
+ case '\f':
190
+ fbuffer_append(buffer, "\\f", 2);
191
+ break;
192
+ case '\b':
193
+ fbuffer_append(buffer, "\\b", 2);
194
+ break;
195
+ default:
196
+ unicode_escape_to_buffer(buffer, buf, (UTF16) ch);
197
+ break;
198
+ }
199
+ }
200
+ }
201
+ } else if (ch > UNI_MAX_UTF16) {
202
+ #if UNI_STRICT_CONVERSION
203
+ source -= (extraBytesToRead+1); /* return to the start */
204
+ rb_raise(rb_path2class("JSON::GeneratorError"),
205
+ "source sequence is illegal/malformed utf8");
206
+ #else
207
+ unicode_escape_to_buffer(buffer, buf, UNI_REPLACEMENT_CHAR);
208
+ #endif
209
+ } else {
210
+ /* target is a character in range 0xFFFF - 0x10FFFF. */
211
+ ch -= halfBase;
212
+ unicode_escape_to_buffer(buffer, buf, (UTF16)((ch >> halfShift) + UNI_SUR_HIGH_START));
213
+ unicode_escape_to_buffer(buffer, buf, (UTF16)((ch & halfMask) + UNI_SUR_LOW_START));
214
+ }
89
215
  }
90
- if (RSTRING_LEN(state->object_nl)) {
91
- rb_str_buf_append(buf, rb_str_times(state->indent, Vdepth));
216
+ }
217
+
218
+ /* Converts string to a JSON string in FBuffer buffer, where only the
219
+ * characters required by the JSON standard are JSON escaped. The remaining
220
+ * characters (should be UTF8) are just passed through and appended to the
221
+ * result. */
222
+ static void convert_UTF8_to_JSON(FBuffer *buffer, VALUE string)
223
+ {
224
+ const char *ptr = RSTRING_PTR(string), *p;
225
+ int len = RSTRING_LEN(string), start = 0, end = 0;
226
+ const char *escape = NULL;
227
+ int escape_len;
228
+ unsigned char c;
229
+ char buf[6] = { '\\', 'u' };
230
+
231
+ for (start = 0, end = 0; end < len;) {
232
+ p = ptr + end;
233
+ c = (unsigned char) *p;
234
+ if (c < 0x20) {
235
+ switch (c) {
236
+ case '\n':
237
+ escape = "\\n";
238
+ escape_len = 2;
239
+ break;
240
+ case '\r':
241
+ escape = "\\r";
242
+ escape_len = 2;
243
+ break;
244
+ case '\t':
245
+ escape = "\\t";
246
+ escape_len = 2;
247
+ break;
248
+ case '\f':
249
+ escape = "\\f";
250
+ escape_len = 2;
251
+ break;
252
+ case '\b':
253
+ escape = "\\b";
254
+ escape_len = 2;
255
+ break;
256
+ default:
257
+ unicode_escape(buf, (UTF16) *p);
258
+ escape = buf;
259
+ escape_len = 6;
260
+ break;
261
+ }
262
+ } else {
263
+ switch (c) {
264
+ case '\\':
265
+ escape = "\\\\";
266
+ escape_len = 2;
267
+ break;
268
+ case '"':
269
+ escape = "\\\"";
270
+ escape_len = 2;
271
+ break;
272
+ default:
273
+ end++;
274
+ continue;
275
+ break;
276
+ }
277
+ }
278
+ fbuffer_append(buffer, ptr + start, end - start);
279
+ fbuffer_append(buffer, escape, escape_len);
280
+ start = ++end;
281
+ escape = NULL;
92
282
  }
93
- json = rb_funcall(rb_funcall(key, i_to_s, 0), i_to_json, 2, Vstate, Vdepth);
94
- Check_Type(json, T_STRING);
95
- rb_str_buf_append(buf, json);
96
- OBJ_INFECT(buf, json);
97
- if (RSTRING_LEN(state->space_before)) {
98
- rb_str_buf_append(buf, state->space_before);
283
+ fbuffer_append(buffer, ptr + start, end - start);
284
+ }
285
+
286
+ static char *fstrndup(const char *ptr, int len) {
287
+ char *result;
288
+ if (len <= 0) return NULL;
289
+ result = ALLOC_N(char, len);
290
+ memccpy(result, ptr, 0, len);
291
+ return result;
292
+ }
293
+
294
+ /* fbuffer implementation */
295
+
296
+ static FBuffer *fbuffer_alloc()
297
+ {
298
+ FBuffer *fb = ALLOC(FBuffer);
299
+ memset((void *) fb, 0, sizeof(FBuffer));
300
+ fb->initial_length = FBUFFER_INITIAL_LENGTH;
301
+ return fb;
302
+ }
303
+
304
+ static FBuffer *fbuffer_alloc_with_length(unsigned int initial_length)
305
+ {
306
+ FBuffer *fb;
307
+ assert(initial_length > 0);
308
+ fb = ALLOC(FBuffer);
309
+ memset((void *) fb, 0, sizeof(FBuffer));
310
+ fb->initial_length = initial_length;
311
+ return fb;
312
+ }
313
+
314
+ static void fbuffer_free(FBuffer *fb)
315
+ {
316
+ if (fb->ptr) ruby_xfree(fb->ptr);
317
+ ruby_xfree(fb);
318
+ }
319
+
320
+ static void fbuffer_free_only_buffer(FBuffer *fb)
321
+ {
322
+ ruby_xfree(fb);
323
+ }
324
+
325
+ static void fbuffer_clear(FBuffer *fb)
326
+ {
327
+ fb->len = 0;
328
+ }
329
+
330
+ static void fbuffer_inc_capa(FBuffer *fb, unsigned int requested)
331
+ {
332
+ unsigned int required;
333
+
334
+ if (!fb->ptr) {
335
+ fb->ptr = ALLOC_N(char, fb->initial_length);
336
+ fb->capa = fb->initial_length;
99
337
  }
100
- rb_str_buf_cat2(buf, ":");
101
- if (RSTRING_LEN(state->space)) rb_str_buf_append(buf, state->space);
102
- json = rb_funcall(value, i_to_json, 2, Vstate, Vdepth);
103
- Check_Type(json, T_STRING);
104
- state->flag = 1;
105
- rb_str_buf_append(buf, json);
106
- OBJ_INFECT(buf, json);
107
- state->depth = Vdepth;
108
- state->memo = buf;
109
- return ST_CONTINUE;
110
- }
111
-
112
- inline static VALUE mHash_json_transfrom(VALUE self, VALUE Vstate, VALUE Vdepth) {
113
- long depth, len = RHASH_SIZE(self);
114
- VALUE result;
115
- GET_STATE(Vstate);
116
-
117
- depth = 1 + FIX2LONG(Vdepth);
118
- result = rb_str_buf_new(len);
119
- state->memo = result;
120
- state->depth = LONG2FIX(depth);
121
- state->flag = 0;
122
- rb_str_buf_cat2(result, "{");
123
- if (RSTRING_LEN(state->object_nl)) rb_str_buf_append(result, state->object_nl);
124
- rb_hash_foreach(self, hash_to_json_state_i, Vstate);
125
- if (RSTRING_LEN(state->object_nl)) rb_str_buf_append(result, state->object_nl);
126
- if (RSTRING_LEN(state->object_nl)) {
127
- rb_str_buf_append(result, rb_str_times(state->indent, Vdepth));
338
+
339
+ for (required = fb->capa; requested > required - fb->len; required <<= 1);
340
+
341
+ if (required > fb->capa) {
342
+ fb->ptr = (char *) REALLOC_N((long*) fb->ptr, char, required);
343
+ fb->capa = required;
128
344
  }
129
- rb_str_buf_cat2(result, "}");
130
- return result;
131
345
  }
132
346
 
133
- static int hash_to_json_i(VALUE key, VALUE value, VALUE buf)
347
+ static void fbuffer_append(FBuffer *fb, const char *newstr, unsigned int len)
134
348
  {
135
- VALUE tmp;
349
+ if (len > 0) {
350
+ fbuffer_inc_capa(fb, len);
351
+ MEMCPY(fb->ptr + fb->len, newstr, char, len);
352
+ fb->len += len;
353
+ }
354
+ }
136
355
 
137
- if (key == Qundef) return ST_CONTINUE;
138
- if (RSTRING_LEN(buf) > 1) rb_str_buf_cat2(buf, ",");
139
- tmp = rb_funcall(rb_funcall(key, i_to_s, 0), i_to_json, 0);
140
- Check_Type(tmp, T_STRING);
141
- rb_str_buf_append(buf, tmp);
142
- OBJ_INFECT(buf, tmp);
143
- rb_str_buf_cat2(buf, ":");
144
- tmp = rb_funcall(value, i_to_json, 0);
145
- Check_Type(tmp, T_STRING);
146
- rb_str_buf_append(buf, tmp);
147
- OBJ_INFECT(buf, tmp);
356
+ static void fbuffer_append_char(FBuffer *fb, char newchr)
357
+ {
358
+ fbuffer_inc_capa(fb, 1);
359
+ *(fb->ptr + fb->len) = newchr;
360
+ fb->len++;
361
+ }
148
362
 
149
- return ST_CONTINUE;
363
+ static void freverse(char *start, char *end)
364
+ {
365
+ char c;
366
+
367
+ while (end > start) {
368
+ c = *end, *end-- = *start, *start++ = c;
369
+ }
370
+ }
371
+
372
+ static int fltoa(long number, char *buf)
373
+ {
374
+ static char digits[] = "0123456789";
375
+ long sign = number;
376
+ char* tmp = buf;
377
+
378
+ if (sign < 0) number = -number;
379
+ do *tmp++ = digits[number % 10]; while (number /= 10);
380
+ if (sign < 0) *tmp++ = '-';
381
+ freverse(buf, tmp - 1);
382
+ return tmp - buf;
150
383
  }
151
384
 
385
+ static void fbuffer_append_long(FBuffer *fb, long number)
386
+ {
387
+ char buf[20];
388
+ int len = fltoa(number, buf);
389
+ fbuffer_append(fb, buf, len);
390
+ }
391
+
392
+ static FBuffer *fbuffer_dup(FBuffer *fb)
393
+ {
394
+ int len = fb->len;
395
+ FBuffer *result;
396
+
397
+ if (len > 0) {
398
+ result = fbuffer_alloc_with_length(len);
399
+ fbuffer_append(result, FBUFFER_PAIR(fb));
400
+ } else {
401
+ result = fbuffer_alloc();
402
+ }
403
+ return result;
404
+ }
405
+
406
+ /*
407
+ * Document-module: JSON::Ext::Generator
408
+ *
409
+ * This is the JSON generator implemented as a C extension. It can be
410
+ * configured to be used by setting
411
+ *
412
+ * JSON.generator = JSON::Ext::Generator
413
+ *
414
+ * with the method generator= in JSON.
415
+ *
416
+ */
417
+
152
418
  /*
153
419
  * call-seq: to_json(state = nil, depth = 0)
154
420
  *
155
- * Returns a JSON string containing a JSON object, that is unparsed from
421
+ * Returns a JSON string containing a JSON object, that is generated from
156
422
  * this Hash instance.
157
423
  * _state_ is a JSON::State object, that can also be used to configure the
158
424
  * produced JSON string output further.
@@ -160,134 +426,27 @@ static int hash_to_json_i(VALUE key, VALUE value, VALUE buf)
160
426
  */
161
427
  static VALUE mHash_to_json(int argc, VALUE *argv, VALUE self)
162
428
  {
163
- VALUE Vstate, Vdepth, result;
164
- long depth;
165
-
166
- rb_scan_args(argc, argv, "02", &Vstate, &Vdepth);
167
- depth = NIL_P(Vdepth) ? 0 : FIX2LONG(Vdepth);
168
- if (NIL_P(Vstate)) {
169
- long len = RHASH_SIZE(self);
170
- result = rb_str_buf_new(len);
171
- rb_str_buf_cat2(result, "{");
172
- rb_hash_foreach(self, hash_to_json_i, result);
173
- rb_str_buf_cat2(result, "}");
174
- } else {
175
- GET_STATE(Vstate);
176
- check_max_nesting(state, depth);
177
- if (state->check_circular) {
178
- VALUE self_id = rb_obj_id(self);
179
- if (RTEST(rb_hash_aref(state->seen, self_id))) {
180
- rb_raise(eCircularDatastructure,
181
- "circular data structures not supported!");
182
- }
183
- rb_hash_aset(state->seen, self_id, Qtrue);
184
- result = mHash_json_transfrom(self, Vstate, LONG2FIX(depth));
185
- rb_hash_delete(state->seen, self_id);
186
- } else {
187
- result = mHash_json_transfrom(self, Vstate, LONG2FIX(depth));
188
- }
189
- }
190
- OBJ_INFECT(result, self);
191
- FORCE_UTF8(result);
192
- return result;
193
- }
429
+ VALUE state, depth;
194
430
 
195
- inline static VALUE mArray_json_transfrom(VALUE self, VALUE Vstate, VALUE Vdepth) {
196
- long i, len = RARRAY_LEN(self);
197
- VALUE shift, result;
198
- long depth = NIL_P(Vdepth) ? 0 : FIX2LONG(Vdepth);
199
- VALUE delim = rb_str_new2(",");
200
- GET_STATE(Vstate);
201
-
202
- check_max_nesting(state, depth);
203
- if (state->check_circular) {
204
- VALUE self_id = rb_obj_id(self);
205
- rb_hash_aset(state->seen, self_id, Qtrue);
206
- result = rb_str_buf_new(len);
207
- if (RSTRING_LEN(state->array_nl)) rb_str_append(delim, state->array_nl);
208
- shift = rb_str_times(state->indent, LONG2FIX(depth + 1));
209
-
210
- rb_str_buf_cat2(result, "[");
211
- OBJ_INFECT(result, self);
212
- rb_str_buf_append(result, state->array_nl);
213
- for (i = 0; i < len; i++) {
214
- VALUE element = RARRAY_PTR(self)[i];
215
- if (RTEST(rb_hash_aref(state->seen, rb_obj_id(element)))) {
216
- rb_raise(eCircularDatastructure,
217
- "circular data structures not supported!");
218
- }
219
- OBJ_INFECT(result, element);
220
- if (i > 0) rb_str_buf_append(result, delim);
221
- rb_str_buf_append(result, shift);
222
- element = rb_funcall(element, i_to_json, 2, Vstate, LONG2FIX(depth + 1));
223
- Check_Type(element, T_STRING);
224
- rb_str_buf_append(result, element);
225
- }
226
- if (RSTRING_LEN(state->array_nl)) {
227
- rb_str_buf_append(result, state->array_nl);
228
- rb_str_buf_append(result, rb_str_times(state->indent, LONG2FIX(depth)));
229
- }
230
- rb_str_buf_cat2(result, "]");
231
- rb_hash_delete(state->seen, self_id);
232
- } else {
233
- result = rb_str_buf_new(len);
234
- OBJ_INFECT(result, self);
235
- if (RSTRING_LEN(state->array_nl)) rb_str_append(delim, state->array_nl);
236
- shift = rb_str_times(state->indent, LONG2FIX(depth + 1));
237
-
238
- rb_str_buf_cat2(result, "[");
239
- rb_str_buf_append(result, state->array_nl);
240
- for (i = 0; i < len; i++) {
241
- VALUE element = RARRAY_PTR(self)[i];
242
- OBJ_INFECT(result, element);
243
- if (i > 0) rb_str_buf_append(result, delim);
244
- rb_str_buf_append(result, shift);
245
- element = rb_funcall(element, i_to_json, 2, Vstate, LONG2FIX(depth + 1));
246
- Check_Type(element, T_STRING);
247
- rb_str_buf_append(result, element);
248
- }
249
- rb_str_buf_append(result, state->array_nl);
250
- if (RSTRING_LEN(state->array_nl)) {
251
- rb_str_buf_append(result, rb_str_times(state->indent, LONG2FIX(depth)));
252
- }
253
- rb_str_buf_cat2(result, "]");
254
- }
255
- return result;
431
+ rb_scan_args(argc, argv, "02", &state, &depth);
432
+ state = cState_from_state_s(cState, state);
433
+ return cState_partial_generate(state, self, depth);
256
434
  }
257
435
 
258
436
  /*
259
437
  * call-seq: to_json(state = nil, depth = 0)
260
438
  *
261
- * Returns a JSON string containing a JSON array, that is unparsed from
439
+ * Returns a JSON string containing a JSON array, that is generated from
262
440
  * this Array instance.
263
441
  * _state_ is a JSON::State object, that can also be used to configure the
264
442
  * produced JSON string output further.
265
443
  * _depth_ is used to find out nesting depth, to indent accordingly.
266
444
  */
267
445
  static VALUE mArray_to_json(int argc, VALUE *argv, VALUE self) {
268
- VALUE Vstate, Vdepth, result;
269
-
270
- rb_scan_args(argc, argv, "02", &Vstate, &Vdepth);
271
- if (NIL_P(Vstate)) {
272
- long i, len = RARRAY_LEN(self);
273
- result = rb_str_buf_new(2 + 2 * len);
274
- rb_str_buf_cat2(result, "[");
275
- OBJ_INFECT(result, self);
276
- for (i = 0; i < len; i++) {
277
- VALUE element = RARRAY_PTR(self)[i];
278
- OBJ_INFECT(result, element);
279
- if (i > 0) rb_str_buf_cat2(result, ",");
280
- element = rb_funcall(element, i_to_json, 0);
281
- Check_Type(element, T_STRING);
282
- rb_str_buf_append(result, element);
283
- }
284
- rb_str_buf_cat2(result, "]");
285
- } else {
286
- result = mArray_json_transfrom(self, Vstate, Vdepth);
287
- }
288
- OBJ_INFECT(result, self);
289
- FORCE_UTF8(result);
290
- return result;
446
+ VALUE state, depth;
447
+ rb_scan_args(argc, argv, "02", &state, &depth);
448
+ state = cState_from_state_s(cState, state);
449
+ return cState_partial_generate(state, self, depth);
291
450
  }
292
451
 
293
452
  /*
@@ -297,9 +456,10 @@ static VALUE mArray_to_json(int argc, VALUE *argv, VALUE self) {
297
456
  */
298
457
  static VALUE mInteger_to_json(int argc, VALUE *argv, VALUE self)
299
458
  {
300
- VALUE result = rb_funcall(self, i_to_s, 0);
301
- FORCE_UTF8(result);
302
- return result;
459
+ VALUE state, depth;
460
+ rb_scan_args(argc, argv, "02", &state, &depth);
461
+ state = cState_from_state_s(cState, state);
462
+ return cState_partial_generate(state, self, depth);
303
463
  }
304
464
 
305
465
  /*
@@ -309,30 +469,10 @@ static VALUE mInteger_to_json(int argc, VALUE *argv, VALUE self)
309
469
  */
310
470
  static VALUE mFloat_to_json(int argc, VALUE *argv, VALUE self)
311
471
  {
312
- JSON_Generator_State *state = NULL;
313
- VALUE Vstate, rest, tmp, result;
314
- double value = RFLOAT_VALUE(self);
315
- rb_scan_args(argc, argv, "01*", &Vstate, &rest);
316
- if (!NIL_P(Vstate)) Data_Get_Struct(Vstate, JSON_Generator_State, state);
317
- if (isinf(value)) {
318
- if (!state || state->allow_nan) {
319
- result = rb_funcall(self, i_to_s, 0);
320
- } else {
321
- tmp = rb_funcall(self, i_to_s, 0);
322
- rb_raise(eGeneratorError, "%u: %s not allowed in JSON", __LINE__, StringValueCStr(tmp));
323
- }
324
- } else if (isnan(value)) {
325
- if (!state || state->allow_nan) {
326
- result = rb_funcall(self, i_to_s, 0);
327
- } else {
328
- tmp = rb_funcall(self, i_to_s, 0);
329
- rb_raise(eGeneratorError, "%u: %s not allowed in JSON", __LINE__, StringValueCStr(tmp));
330
- }
331
- } else {
332
- result = rb_funcall(self, i_to_s, 0);
333
- }
334
- FORCE_UTF8(result);
335
- return result;
472
+ VALUE state, depth;
473
+ rb_scan_args(argc, argv, "02", &state, &depth);
474
+ state = cState_from_state_s(cState, state);
475
+ return cState_partial_generate(state, self, depth);
336
476
  }
337
477
 
338
478
  /*
@@ -342,7 +482,6 @@ static VALUE mFloat_to_json(int argc, VALUE *argv, VALUE self)
342
482
  */
343
483
  static VALUE mString_included_s(VALUE self, VALUE modul) {
344
484
  VALUE result = rb_funcall(modul, i_extend, 1, mString_Extend);
345
- FORCE_UTF8(result);
346
485
  return result;
347
486
  }
348
487
 
@@ -355,29 +494,27 @@ static VALUE mString_included_s(VALUE self, VALUE modul) {
355
494
  */
356
495
  static VALUE mString_to_json(int argc, VALUE *argv, VALUE self)
357
496
  {
358
- VALUE result = rb_str_buf_new(RSTRING_LEN(self));
359
- rb_str_buf_cat2(result, "\"");
360
- JSON_convert_UTF8_to_JSON(result, self, strictConversion);
361
- rb_str_buf_cat2(result, "\"");
362
- FORCE_UTF8(result);
363
- return result;
497
+ VALUE state, depth;
498
+ rb_scan_args(argc, argv, "02", &state, &depth);
499
+ state = cState_from_state_s(cState, state);
500
+ return cState_partial_generate(state, self, depth);
364
501
  }
365
502
 
366
503
  /*
367
504
  * call-seq: to_json_raw_object()
368
505
  *
369
506
  * This method creates a raw object hash, that can be nested into
370
- * other data structures and will be unparsed as a raw string. This
507
+ * other data structures and will be generated as a raw string. This
371
508
  * method should be used, if you want to convert raw strings to JSON
372
509
  * instead of UTF-8 strings, e. g. binary data.
373
510
  */
374
- static VALUE mString_to_json_raw_object(VALUE self) {
511
+ static VALUE mString_to_json_raw_object(VALUE self)
512
+ {
375
513
  VALUE ary;
376
514
  VALUE result = rb_hash_new();
377
515
  rb_hash_aset(result, rb_funcall(mJSON, i_create_id, 0), rb_class_name(rb_obj_class(self)));
378
516
  ary = rb_funcall(self, i_unpack, 1, rb_str_new2("C*"));
379
517
  rb_hash_aset(result, rb_str_new2("raw"), ary);
380
- FORCE_UTF8(result);
381
518
  return result;
382
519
  }
383
520
 
@@ -387,12 +524,11 @@ static VALUE mString_to_json_raw_object(VALUE self) {
387
524
  * This method creates a JSON text from the result of a call to
388
525
  * to_json_raw_object of this String.
389
526
  */
390
- static VALUE mString_to_json_raw(int argc, VALUE *argv, VALUE self) {
391
- VALUE result, obj = mString_to_json_raw_object(self);
527
+ static VALUE mString_to_json_raw(int argc, VALUE *argv, VALUE self)
528
+ {
529
+ VALUE obj = mString_to_json_raw_object(self);
392
530
  Check_Type(obj, T_HASH);
393
- result = mHash_to_json(argc, argv, obj);
394
- FORCE_UTF8(result);
395
- return result;
531
+ return mHash_to_json(argc, argv, obj);
396
532
  }
397
533
 
398
534
  /*
@@ -401,7 +537,8 @@ static VALUE mString_to_json_raw(int argc, VALUE *argv, VALUE self) {
401
537
  * Raw Strings are JSON Objects (the raw bytes are stored in an array for the
402
538
  * key "raw"). The Ruby String can be created by this module method.
403
539
  */
404
- static VALUE mString_Extend_json_create(VALUE self, VALUE o) {
540
+ static VALUE mString_Extend_json_create(VALUE self, VALUE o)
541
+ {
405
542
  VALUE ary;
406
543
  Check_Type(o, T_HASH);
407
544
  ary = rb_hash_aref(o, rb_str_new2("raw"));
@@ -409,38 +546,41 @@ static VALUE mString_Extend_json_create(VALUE self, VALUE o) {
409
546
  }
410
547
 
411
548
  /*
412
- * call-seq: to_json(state = nil, depth = 0)
549
+ * call-seq: to_json(*)
413
550
  *
414
551
  * Returns a JSON string for true: 'true'.
415
552
  */
416
553
  static VALUE mTrueClass_to_json(int argc, VALUE *argv, VALUE self)
417
554
  {
418
- VALUE result = rb_str_new2("true");
419
- FORCE_UTF8(result);
420
- return result;
555
+ VALUE state, depth;
556
+ rb_scan_args(argc, argv, "02", &state, &depth);
557
+ state = cState_from_state_s(cState, state);
558
+ return cState_partial_generate(state, self, depth);
421
559
  }
422
560
 
423
561
  /*
424
- * call-seq: to_json(state = nil, depth = 0)
562
+ * call-seq: to_json(*)
425
563
  *
426
564
  * Returns a JSON string for false: 'false'.
427
565
  */
428
566
  static VALUE mFalseClass_to_json(int argc, VALUE *argv, VALUE self)
429
567
  {
430
- VALUE result = rb_str_new2("false");
431
- FORCE_UTF8(result);
432
- return result;
568
+ VALUE state, depth;
569
+ rb_scan_args(argc, argv, "02", &state, &depth);
570
+ state = cState_from_state_s(cState, state);
571
+ return cState_partial_generate(state, self, depth);
433
572
  }
434
573
 
435
574
  /*
436
- * call-seq: to_json(state = nil, depth = 0)
575
+ * call-seq: to_json(*)
437
576
  *
438
577
  */
439
578
  static VALUE mNilClass_to_json(int argc, VALUE *argv, VALUE self)
440
579
  {
441
- VALUE result = rb_str_new2("null");
442
- FORCE_UTF8(result);
443
- return result;
580
+ VALUE state, depth;
581
+ rb_scan_args(argc, argv, "02", &state, &depth);
582
+ state = cState_from_state_s(cState, state);
583
+ return cState_partial_generate(state, self, depth);
444
584
  }
445
585
 
446
586
  /*
@@ -452,30 +592,25 @@ static VALUE mNilClass_to_json(int argc, VALUE *argv, VALUE self)
452
592
  */
453
593
  static VALUE mObject_to_json(int argc, VALUE *argv, VALUE self)
454
594
  {
455
- VALUE result, string = rb_funcall(self, i_to_s, 0);
595
+ VALUE state, depth;
596
+ VALUE string = rb_funcall(self, i_to_s, 0);
597
+ rb_scan_args(argc, argv, "02", &state, &depth);
456
598
  Check_Type(string, T_STRING);
457
- result = mString_to_json(argc, argv, string);
458
- FORCE_UTF8(result);
459
- return result;
599
+ state = cState_from_state_s(cState, state);
600
+ return cState_partial_generate(state, string, depth);
460
601
  }
461
602
 
462
- /*
463
- * Document-class: JSON::Ext::Generator::State
464
- *
465
- * This class is used to create State instances, that are use to hold data
466
- * while generating a JSON text from a a Ruby data structure.
467
- */
468
-
469
- static void State_mark(JSON_Generator_State *state)
603
+ static void State_free(JSON_Generator_State *state)
470
604
  {
471
- rb_gc_mark_maybe(state->indent);
472
- rb_gc_mark_maybe(state->space);
473
- rb_gc_mark_maybe(state->space_before);
474
- rb_gc_mark_maybe(state->object_nl);
475
- rb_gc_mark_maybe(state->array_nl);
476
- rb_gc_mark_maybe(state->seen);
477
- rb_gc_mark_maybe(state->memo);
478
- rb_gc_mark_maybe(state->depth);
605
+ if (state->indent) ruby_xfree(state->indent);
606
+ if (state->space) ruby_xfree(state->space);
607
+ if (state->space_before) ruby_xfree(state->space_before);
608
+ if (state->object_nl) ruby_xfree(state->object_nl);
609
+ if (state->array_nl) ruby_xfree(state->array_nl);
610
+ if (state->array_delim) fbuffer_free(state->array_delim);
611
+ if (state->object_delim) fbuffer_free(state->object_delim);
612
+ if (state->object_delim2) fbuffer_free(state->object_delim2);
613
+ ruby_xfree(state);
479
614
  }
480
615
 
481
616
  static JSON_Generator_State *State_allocate()
@@ -487,7 +622,7 @@ static JSON_Generator_State *State_allocate()
487
622
  static VALUE cState_s_allocate(VALUE klass)
488
623
  {
489
624
  JSON_Generator_State *state = State_allocate();
490
- return Data_Wrap_Struct(klass, State_mark, -1, state);
625
+ return Data_Wrap_Struct(klass, NULL, State_free, state);
491
626
  }
492
627
 
493
628
  /*
@@ -508,39 +643,47 @@ static VALUE cState_configure(VALUE self, VALUE opts)
508
643
  opts = tmp;
509
644
  tmp = rb_hash_aref(opts, ID2SYM(i_indent));
510
645
  if (RTEST(tmp)) {
646
+ int len;
511
647
  Check_Type(tmp, T_STRING);
512
- state->indent = tmp;
648
+ len = RSTRING_LEN(tmp);
649
+ state->indent = fstrndup(RSTRING_PTR(tmp), len);
650
+ state->indent_len = len;
513
651
  }
514
652
  tmp = rb_hash_aref(opts, ID2SYM(i_space));
515
653
  if (RTEST(tmp)) {
654
+ int len;
516
655
  Check_Type(tmp, T_STRING);
517
- state->space = tmp;
656
+ len = RSTRING_LEN(tmp);
657
+ state->space = fstrndup(RSTRING_PTR(tmp), len);
658
+ state->space_len = len;
518
659
  }
519
660
  tmp = rb_hash_aref(opts, ID2SYM(i_space_before));
520
661
  if (RTEST(tmp)) {
662
+ int len;
521
663
  Check_Type(tmp, T_STRING);
522
- state->space_before = tmp;
664
+ len = RSTRING_LEN(tmp);
665
+ state->space_before = fstrndup(RSTRING_PTR(tmp), len);
666
+ state->space_before_len = len;
523
667
  }
524
668
  tmp = rb_hash_aref(opts, ID2SYM(i_array_nl));
525
669
  if (RTEST(tmp)) {
670
+ int len;
526
671
  Check_Type(tmp, T_STRING);
527
- state->array_nl = tmp;
672
+ len = RSTRING_LEN(tmp);
673
+ state->array_nl = fstrndup(RSTRING_PTR(tmp), len);
674
+ state->array_nl_len = len;
528
675
  }
529
676
  tmp = rb_hash_aref(opts, ID2SYM(i_object_nl));
530
677
  if (RTEST(tmp)) {
678
+ int len;
531
679
  Check_Type(tmp, T_STRING);
532
- state->object_nl = tmp;
533
- }
534
- tmp = ID2SYM(i_check_circular);
535
- if (st_lookup(RHASH_TBL(opts), tmp, 0)) {
536
- tmp = rb_hash_aref(opts, ID2SYM(i_check_circular));
537
- state->check_circular = RTEST(tmp);
538
- } else {
539
- state->check_circular = 1;
680
+ len = RSTRING_LEN(tmp);
681
+ state->object_nl = fstrndup(RSTRING_PTR(tmp), len);
682
+ state->object_nl_len = len;
540
683
  }
541
684
  tmp = ID2SYM(i_max_nesting);
542
685
  state->max_nesting = 19;
543
- if (st_lookup(RHASH_TBL(opts), tmp, 0)) {
686
+ if (option_given_p(opts, tmp)) {
544
687
  VALUE max_nesting = rb_hash_aref(opts, tmp);
545
688
  if (RTEST(max_nesting)) {
546
689
  Check_Type(max_nesting, T_FIXNUM);
@@ -551,6 +694,8 @@ static VALUE cState_configure(VALUE self, VALUE opts)
551
694
  }
552
695
  tmp = rb_hash_aref(opts, ID2SYM(i_allow_nan));
553
696
  state->allow_nan = RTEST(tmp);
697
+ tmp = rb_hash_aref(opts, ID2SYM(i_ascii_only));
698
+ state->ascii_only = RTEST(tmp);
554
699
  return self;
555
700
  }
556
701
 
@@ -564,17 +709,242 @@ static VALUE cState_to_h(VALUE self)
564
709
  {
565
710
  VALUE result = rb_hash_new();
566
711
  GET_STATE(self);
567
- rb_hash_aset(result, ID2SYM(i_indent), state->indent);
568
- rb_hash_aset(result, ID2SYM(i_space), state->space);
569
- rb_hash_aset(result, ID2SYM(i_space_before), state->space_before);
570
- rb_hash_aset(result, ID2SYM(i_object_nl), state->object_nl);
571
- rb_hash_aset(result, ID2SYM(i_array_nl), state->array_nl);
572
- rb_hash_aset(result, ID2SYM(i_check_circular), state->check_circular ? Qtrue : Qfalse);
712
+ rb_hash_aset(result, ID2SYM(i_indent), rb_str_new(state->indent, state->indent_len));
713
+ rb_hash_aset(result, ID2SYM(i_space), rb_str_new(state->space, state->space_len));
714
+ rb_hash_aset(result, ID2SYM(i_space_before), rb_str_new(state->space_before, state->space_before_len));
715
+ rb_hash_aset(result, ID2SYM(i_object_nl), rb_str_new(state->object_nl, state->object_nl_len));
716
+ rb_hash_aset(result, ID2SYM(i_array_nl), rb_str_new(state->array_nl, state->array_nl_len));
573
717
  rb_hash_aset(result, ID2SYM(i_allow_nan), state->allow_nan ? Qtrue : Qfalse);
718
+ rb_hash_aset(result, ID2SYM(i_ascii_only), state->ascii_only ? Qtrue : Qfalse);
574
719
  rb_hash_aset(result, ID2SYM(i_max_nesting), LONG2FIX(state->max_nesting));
575
720
  return result;
576
721
  }
577
722
 
723
+ /*
724
+ * call-seq: [](name)
725
+ *
726
+ * Return the value returned by method +name+.
727
+ */
728
+ static VALUE cState_aref(VALUE self, VALUE name)
729
+ {
730
+ GET_STATE(self);
731
+ if (RTEST(rb_funcall(self, i_respond_to_p, 1, name))) {
732
+ return rb_funcall(self, i_send, 1, name);
733
+ } else {
734
+ return Qnil;
735
+ }
736
+ }
737
+
738
+ static void generate_json(FBuffer *buffer, VALUE Vstate, JSON_Generator_State *state, VALUE obj, long depth)
739
+ {
740
+ VALUE tmp;
741
+ switch (TYPE(obj)) {
742
+ case T_HASH:
743
+ {
744
+ char *object_nl = state->object_nl;
745
+ long object_nl_len = state->object_nl_len;
746
+ char *indent = state->indent;
747
+ long indent_len = state->indent_len;
748
+ long max_nesting = state->max_nesting;
749
+ char *delim = FBUFFER_PTR(state->object_delim);
750
+ long delim_len = FBUFFER_LEN(state->object_delim);
751
+ char *delim2 = FBUFFER_PTR(state->object_delim2);
752
+ long delim2_len = FBUFFER_LEN(state->object_delim2);
753
+ int i, j;
754
+ VALUE key, key_to_s, keys;
755
+ depth++;
756
+ if (max_nesting != 0 && depth > max_nesting) {
757
+ fbuffer_free(buffer);
758
+ rb_raise(eNestingError, "nesting of %ld is too deep", depth);
759
+ }
760
+ fbuffer_append_char(buffer, '{');
761
+ keys = rb_funcall(obj, rb_intern("keys"), 0);
762
+ for(i = 0; i < RARRAY_LEN(keys); i++) {
763
+ if (i > 0) fbuffer_append(buffer, delim, delim_len);
764
+ if (object_nl) {
765
+ fbuffer_append(buffer, object_nl, object_nl_len);
766
+ }
767
+ if (indent) {
768
+ for (j = 0; j < depth; j++) {
769
+ fbuffer_append(buffer, indent, indent_len);
770
+ }
771
+ }
772
+ key = rb_ary_entry(keys, i);
773
+ key_to_s = rb_funcall(key, i_to_s, 0);
774
+ Check_Type(key_to_s, T_STRING);
775
+ generate_json(buffer, Vstate, state, key_to_s, depth);
776
+ fbuffer_append(buffer, delim2, delim2_len);
777
+ generate_json(buffer, Vstate, state, rb_hash_aref(obj, key), depth);
778
+ }
779
+ depth--;
780
+ if (object_nl) {
781
+ fbuffer_append(buffer, object_nl, object_nl_len);
782
+ if (indent) {
783
+ for (j = 0; j < depth; j++) {
784
+ fbuffer_append(buffer, indent, indent_len);
785
+ }
786
+ }
787
+ }
788
+ fbuffer_append_char(buffer, '}');
789
+ }
790
+ break;
791
+ case T_ARRAY:
792
+ {
793
+ char *array_nl = state->array_nl;
794
+ long array_nl_len = state->array_nl_len;
795
+ char *indent = state->indent;
796
+ long indent_len = state->indent_len;
797
+ long max_nesting = state->max_nesting;
798
+ char *delim = FBUFFER_PTR(state->array_delim);
799
+ long delim_len = FBUFFER_LEN(state->array_delim);
800
+ int i, j;
801
+ depth++;
802
+ if (max_nesting != 0 && depth > max_nesting) {
803
+ fbuffer_free(buffer);
804
+ rb_raise(eNestingError, "nesting of %ld is too deep", depth);
805
+ }
806
+ fbuffer_append_char(buffer, '[');
807
+ if (array_nl) fbuffer_append(buffer, array_nl, array_nl_len);
808
+ for(i = 0; i < RARRAY_LEN(obj); i++) {
809
+ if (i > 0) fbuffer_append(buffer, delim, delim_len);
810
+ if (indent) {
811
+ for (j = 0; j < depth; j++) {
812
+ fbuffer_append(buffer, indent, indent_len);
813
+ }
814
+ }
815
+ generate_json(buffer, Vstate, state, rb_ary_entry(obj, i), depth);
816
+ }
817
+ depth--;
818
+ if (array_nl) {
819
+ fbuffer_append(buffer, array_nl, array_nl_len);
820
+ if (indent) {
821
+ for (j = 0; j < depth; j++) {
822
+ fbuffer_append(buffer, indent, indent_len);
823
+ }
824
+ }
825
+ }
826
+ fbuffer_append_char(buffer, ']');
827
+ }
828
+ break;
829
+ case T_STRING:
830
+ fbuffer_append_char(buffer, '"');
831
+ #ifdef HAVE_RUBY_ENCODING_H
832
+ obj = rb_funcall(obj, i_encode, 1, CEncoding_UTF_8);
833
+ #endif
834
+ if (state->ascii_only) {
835
+ convert_UTF8_to_JSON_ASCII(buffer, obj);
836
+ } else {
837
+ convert_UTF8_to_JSON(buffer, obj);
838
+ }
839
+ fbuffer_append_char(buffer, '"');
840
+ break;
841
+ case T_NIL:
842
+ fbuffer_append(buffer, "null", 4);
843
+ break;
844
+ case T_FALSE:
845
+ fbuffer_append(buffer, "false", 5);
846
+ break;
847
+ case T_TRUE:
848
+ fbuffer_append(buffer, "true", 4);
849
+ break;
850
+ case T_FIXNUM:
851
+ fbuffer_append_long(buffer, FIX2LONG(obj));
852
+ break;
853
+ case T_BIGNUM:
854
+ tmp = rb_funcall(obj, i_to_s, 0);
855
+ fbuffer_append(buffer, RSTRING_PAIR(tmp));
856
+ break;
857
+ case T_FLOAT:
858
+ {
859
+ double value = RFLOAT_VALUE(obj);
860
+ char allow_nan = state->allow_nan;
861
+ tmp = rb_funcall(obj, i_to_s, 0);
862
+ if (!allow_nan) {
863
+ if (isinf(value)) {
864
+ fbuffer_free(buffer);
865
+ rb_raise(eGeneratorError, "%u: %s not allowed in JSON", __LINE__, StringValueCStr(tmp));
866
+ } else if (isnan(value)) {
867
+ fbuffer_free(buffer);
868
+ rb_raise(eGeneratorError, "%u: %s not allowed in JSON", __LINE__, StringValueCStr(tmp));
869
+ }
870
+ }
871
+ fbuffer_append(buffer, RSTRING_PAIR(tmp));
872
+ }
873
+ break;
874
+ default:
875
+ if (rb_respond_to(obj, i_to_json)) {
876
+ tmp = rb_funcall(obj, i_to_json, 2, Vstate, INT2FIX(depth + 1));
877
+ Check_Type(tmp, T_STRING);
878
+ fbuffer_append(buffer, RSTRING_PAIR(tmp));
879
+ } else {
880
+ tmp = rb_funcall(obj, i_to_s, 0);
881
+ Check_Type(tmp, T_STRING);
882
+ generate_json(buffer, Vstate, state, tmp, depth + 1);
883
+ }
884
+ break;
885
+ }
886
+ }
887
+
888
+ /*
889
+ * call-seq: partial_generate(obj)
890
+ *
891
+ * Generates a part of a JSON document from object +obj+ and returns the
892
+ * result.
893
+ */
894
+ static VALUE cState_partial_generate(VALUE self, VALUE obj, VALUE depth)
895
+ {
896
+ VALUE result;
897
+ FBuffer *buffer = fbuffer_alloc();
898
+ GET_STATE(self);
899
+
900
+ if (state->object_delim) {
901
+ fbuffer_clear(state->object_delim);
902
+ } else {
903
+ state->object_delim = fbuffer_alloc_with_length(16);
904
+ }
905
+ fbuffer_append_char(state->object_delim, ',');
906
+ if (state->object_delim2) {
907
+ fbuffer_clear(state->object_delim2);
908
+ } else {
909
+ state->object_delim2 = fbuffer_alloc_with_length(16);
910
+ }
911
+ fbuffer_append_char(state->object_delim2, ':');
912
+ if (state->space) fbuffer_append(state->object_delim2, state->space, state->space_len);
913
+
914
+ if (state->array_delim) {
915
+ fbuffer_clear(state->array_delim);
916
+ } else {
917
+ state->array_delim = fbuffer_alloc_with_length(16);
918
+ }
919
+ fbuffer_append_char(state->array_delim, ',');
920
+ if (state->array_nl) fbuffer_append(state->array_delim, state->array_nl, state->array_nl_len);
921
+
922
+ generate_json(buffer, self, state, obj, NIL_P(depth) ? 0 : FIX2INT(depth));
923
+ result = rb_str_new(FBUFFER_PAIR(buffer));
924
+ fbuffer_free(buffer);
925
+ FORCE_UTF8(result);
926
+ return result;
927
+ }
928
+
929
+ /*
930
+ * call-seq: generate(obj)
931
+ *
932
+ * Generates a valid JSON document from object +obj+ and returns the
933
+ * result. If no valid JSON document can be created this method raises a
934
+ * GeneratorError exception.
935
+ */
936
+ static VALUE cState_generate(VALUE self, VALUE obj)
937
+ {
938
+ VALUE result = cState_partial_generate(self, obj, Qnil);
939
+ VALUE re, args[2];
940
+ args[0] = rb_str_new2("\\A\\s*(?:\\[.*\\]|\\{.*\\})\\s*\\Z");
941
+ args[1] = CRegexp_MULTILINE;
942
+ re = rb_class_new_instance(2, args, rb_cRegexp);
943
+ if (NIL_P(rb_funcall(re, i_match, 1, result))) {
944
+ rb_raise(eGeneratorError, "only generation of JSON objects or arrays allowed");
945
+ }
946
+ return result;
947
+ }
578
948
 
579
949
  /*
580
950
  * call-seq: new(opts = {})
@@ -588,8 +958,6 @@ static VALUE cState_to_h(VALUE self)
588
958
  * * *space_before*: a string that is put before a : pair delimiter (default: ''),
589
959
  * * *object_nl*: a string that is put at the end of a JSON object (default: ''),
590
960
  * * *array_nl*: a string that is put at the end of a JSON array (default: ''),
591
- * * *check_circular*: true if checking for circular data structures
592
- * should be done, false (the default) otherwise.
593
961
  * * *allow_nan*: true if NaN, Infinity, and -Infinity should be
594
962
  * generated, otherwise an exception is thrown, if these values are
595
963
  * encountered. This options defaults to false.
@@ -598,26 +966,39 @@ static VALUE cState_initialize(int argc, VALUE *argv, VALUE self)
598
966
  {
599
967
  VALUE opts;
600
968
  GET_STATE(self);
601
-
969
+ MEMZERO(state, JSON_Generator_State, 1);
970
+ state->max_nesting = 19;
602
971
  rb_scan_args(argc, argv, "01", &opts);
603
- state->indent = rb_str_new2("");
604
- state->space = rb_str_new2("");
605
- state->space_before = rb_str_new2("");
606
- state->array_nl = rb_str_new2("");
607
- state->object_nl = rb_str_new2("");
608
- if (NIL_P(opts)) {
609
- state->check_circular = 1;
610
- state->allow_nan = 0;
611
- state->max_nesting = 19;
612
- } else {
613
- cState_configure(self, opts);
614
- }
615
- state->seen = rb_hash_new();
616
- state->memo = Qnil;
617
- state->depth = INT2FIX(0);
972
+ if (!NIL_P(opts)) cState_configure(self, opts);
618
973
  return self;
619
974
  }
620
975
 
976
+ /*
977
+ * call-seq: initialize_copy(orig)
978
+ *
979
+ * Initializes this object from orig if it to be duplicated/cloned and returns
980
+ * it.
981
+ */
982
+ static VALUE cState_init_copy(VALUE obj, VALUE orig)
983
+ {
984
+ JSON_Generator_State *objState, *origState;
985
+
986
+ Data_Get_Struct(obj, JSON_Generator_State, objState);
987
+ Data_Get_Struct(orig, JSON_Generator_State, origState);
988
+ if (!objState) rb_raise(rb_eArgError, "unallocated JSON::State");
989
+
990
+ MEMCPY(objState, origState, JSON_Generator_State, 1);
991
+ objState->indent = fstrndup(origState->indent, origState->indent_len);
992
+ objState->space = fstrndup(origState->space, origState->space_len);
993
+ objState->space_before = fstrndup(origState->space_before, origState->space_before_len);
994
+ objState->object_nl = fstrndup(origState->object_nl, origState->object_nl_len);
995
+ objState->array_nl = fstrndup(origState->array_nl, origState->array_nl_len);
996
+ if (origState->array_delim) objState->array_delim = fbuffer_dup(origState->array_delim);
997
+ if (origState->object_delim) objState->object_delim = fbuffer_dup(origState->object_delim);
998
+ if (origState->object_delim2) objState->object_delim2 = fbuffer_dup(origState->object_delim2);
999
+ return obj;
1000
+ }
1001
+
621
1002
  /*
622
1003
  * call-seq: from_state(opts)
623
1004
  *
@@ -632,7 +1013,10 @@ static VALUE cState_from_state_s(VALUE self, VALUE opts)
632
1013
  } else if (rb_obj_is_kind_of(opts, rb_cHash)) {
633
1014
  return rb_funcall(self, i_new, 1, opts);
634
1015
  } else {
635
- return rb_funcall(self, i_new, 0);
1016
+ if (NIL_P(CJSON_SAFE_STATE_PROTOTYPE)) {
1017
+ CJSON_SAFE_STATE_PROTOTYPE = rb_const_get(mJSON, rb_intern("SAFE_STATE_PROTOTYPE"));
1018
+ }
1019
+ return CJSON_SAFE_STATE_PROTOTYPE;
636
1020
  }
637
1021
  }
638
1022
 
@@ -644,7 +1028,7 @@ static VALUE cState_from_state_s(VALUE self, VALUE opts)
644
1028
  static VALUE cState_indent(VALUE self)
645
1029
  {
646
1030
  GET_STATE(self);
647
- return state->indent;
1031
+ return state->indent ? rb_str_new2(state->indent) : rb_str_new2("");
648
1032
  }
649
1033
 
650
1034
  /*
@@ -656,7 +1040,16 @@ static VALUE cState_indent_set(VALUE self, VALUE indent)
656
1040
  {
657
1041
  GET_STATE(self);
658
1042
  Check_Type(indent, T_STRING);
659
- return state->indent = indent;
1043
+ if (RSTRING_LEN(indent) == 0) {
1044
+ if (state->indent) {
1045
+ ruby_xfree(state->indent);
1046
+ state->indent = NULL;
1047
+ }
1048
+ } else {
1049
+ if (state->indent) ruby_xfree(state->indent);
1050
+ state->indent = strdup(RSTRING_PTR(indent));
1051
+ }
1052
+ return Qnil;
660
1053
  }
661
1054
 
662
1055
  /*
@@ -668,7 +1061,7 @@ static VALUE cState_indent_set(VALUE self, VALUE indent)
668
1061
  static VALUE cState_space(VALUE self)
669
1062
  {
670
1063
  GET_STATE(self);
671
- return state->space;
1064
+ return state->space ? rb_str_new2(state->space) : rb_str_new2("");
672
1065
  }
673
1066
 
674
1067
  /*
@@ -681,7 +1074,16 @@ static VALUE cState_space_set(VALUE self, VALUE space)
681
1074
  {
682
1075
  GET_STATE(self);
683
1076
  Check_Type(space, T_STRING);
684
- return state->space = space;
1077
+ if (RSTRING_LEN(space) == 0) {
1078
+ if (state->space) {
1079
+ ruby_xfree(state->space);
1080
+ state->space = NULL;
1081
+ }
1082
+ } else {
1083
+ if (state->space) ruby_xfree(state->space);
1084
+ state->space = strdup(RSTRING_PTR(space));
1085
+ }
1086
+ return Qnil;
685
1087
  }
686
1088
 
687
1089
  /*
@@ -692,7 +1094,7 @@ static VALUE cState_space_set(VALUE self, VALUE space)
692
1094
  static VALUE cState_space_before(VALUE self)
693
1095
  {
694
1096
  GET_STATE(self);
695
- return state->space_before;
1097
+ return state->space_before ? rb_str_new2(state->space_before) : rb_str_new2("");
696
1098
  }
697
1099
 
698
1100
  /*
@@ -704,7 +1106,16 @@ static VALUE cState_space_before_set(VALUE self, VALUE space_before)
704
1106
  {
705
1107
  GET_STATE(self);
706
1108
  Check_Type(space_before, T_STRING);
707
- return state->space_before = space_before;
1109
+ if (RSTRING_LEN(space_before) == 0) {
1110
+ if (state->space_before) {
1111
+ ruby_xfree(state->space_before);
1112
+ state->space_before = NULL;
1113
+ }
1114
+ } else {
1115
+ if (state->space_before) ruby_xfree(state->space_before);
1116
+ state->space_before = strdup(RSTRING_PTR(space_before));
1117
+ }
1118
+ return Qnil;
708
1119
  }
709
1120
 
710
1121
  /*
@@ -716,7 +1127,7 @@ static VALUE cState_space_before_set(VALUE self, VALUE space_before)
716
1127
  static VALUE cState_object_nl(VALUE self)
717
1128
  {
718
1129
  GET_STATE(self);
719
- return state->object_nl;
1130
+ return state->object_nl ? rb_str_new2(state->object_nl) : rb_str_new2("");
720
1131
  }
721
1132
 
722
1133
  /*
@@ -729,7 +1140,16 @@ static VALUE cState_object_nl_set(VALUE self, VALUE object_nl)
729
1140
  {
730
1141
  GET_STATE(self);
731
1142
  Check_Type(object_nl, T_STRING);
732
- return state->object_nl = object_nl;
1143
+ if (RSTRING_LEN(object_nl) == 0) {
1144
+ if (state->object_nl) {
1145
+ ruby_xfree(state->object_nl);
1146
+ state->object_nl = NULL;
1147
+ }
1148
+ } else {
1149
+ if (state->object_nl) ruby_xfree(state->object_nl);
1150
+ state->object_nl = strdup(RSTRING_PTR(object_nl));
1151
+ }
1152
+ return Qnil;
733
1153
  }
734
1154
 
735
1155
  /*
@@ -740,7 +1160,7 @@ static VALUE cState_object_nl_set(VALUE self, VALUE object_nl)
740
1160
  static VALUE cState_array_nl(VALUE self)
741
1161
  {
742
1162
  GET_STATE(self);
743
- return state->array_nl;
1163
+ return state->array_nl ? rb_str_new2(state->array_nl) : rb_str_new2("");
744
1164
  }
745
1165
 
746
1166
  /*
@@ -752,19 +1172,29 @@ static VALUE cState_array_nl_set(VALUE self, VALUE array_nl)
752
1172
  {
753
1173
  GET_STATE(self);
754
1174
  Check_Type(array_nl, T_STRING);
755
- return state->array_nl = array_nl;
1175
+ if (RSTRING_LEN(array_nl) == 0) {
1176
+ if (state->array_nl) {
1177
+ ruby_xfree(state->array_nl);
1178
+ state->array_nl = NULL;
1179
+ }
1180
+ } else {
1181
+ if (state->array_nl) ruby_xfree(state->array_nl);
1182
+ state->array_nl = strdup(RSTRING_PTR(array_nl));
1183
+ }
1184
+ return Qnil;
756
1185
  }
757
1186
 
1187
+
758
1188
  /*
759
- * call-seq: check_circular?
760
- *
761
- * Returns true, if circular data structures should be checked,
762
- * otherwise returns false.
763
- */
1189
+ * call-seq: check_circular?
1190
+ *
1191
+ * Returns true, if circular data structures should be checked,
1192
+ * otherwise returns false.
1193
+ */
764
1194
  static VALUE cState_check_circular_p(VALUE self)
765
1195
  {
766
1196
  GET_STATE(self);
767
- return state->check_circular ? Qtrue : Qfalse;
1197
+ return state->max_nesting ? Qtrue : Qfalse;
768
1198
  }
769
1199
 
770
1200
  /*
@@ -789,8 +1219,7 @@ static VALUE cState_max_nesting_set(VALUE self, VALUE depth)
789
1219
  {
790
1220
  GET_STATE(self);
791
1221
  Check_Type(depth, T_FIXNUM);
792
- state->max_nesting = FIX2LONG(depth);
793
- return Qnil;
1222
+ return state->max_nesting = FIX2LONG(depth);
794
1223
  }
795
1224
 
796
1225
  /*
@@ -806,37 +1235,15 @@ static VALUE cState_allow_nan_p(VALUE self)
806
1235
  }
807
1236
 
808
1237
  /*
809
- * call-seq: seen?(object)
810
- *
811
- * Returns _true_, if _object_ was already seen during this generating run.
812
- */
813
- static VALUE cState_seen_p(VALUE self, VALUE object)
814
- {
815
- GET_STATE(self);
816
- return rb_hash_aref(state->seen, rb_obj_id(object));
817
- }
818
-
819
- /*
820
- * call-seq: remember(object)
1238
+ * call-seq: ascii_only?
821
1239
  *
822
- * Remember _object_, to find out if it was already encountered (if a cyclic
823
- * data structure is rendered).
824
- */
825
- static VALUE cState_remember(VALUE self, VALUE object)
826
- {
827
- GET_STATE(self);
828
- return rb_hash_aset(state->seen, rb_obj_id(object), Qtrue);
829
- }
830
-
831
- /*
832
- * call-seq: forget(object)
833
- *
834
- * Forget _object_ for this generating run.
1240
+ * Returns true, if NaN, Infinity, and -Infinity should be generated, otherwise
1241
+ * returns false.
835
1242
  */
836
- static VALUE cState_forget(VALUE self, VALUE object)
1243
+ static VALUE cState_ascii_only_p(VALUE self)
837
1244
  {
838
1245
  GET_STATE(self);
839
- return rb_hash_delete(state->seen, rb_obj_id(object));
1246
+ return state->ascii_only ? Qtrue : Qfalse;
840
1247
  }
841
1248
 
842
1249
  /*
@@ -845,17 +1252,19 @@ static VALUE cState_forget(VALUE self, VALUE object)
845
1252
  void Init_generator()
846
1253
  {
847
1254
  rb_require("json/common");
1255
+
848
1256
  mJSON = rb_define_module("JSON");
849
1257
  mExt = rb_define_module_under(mJSON, "Ext");
850
1258
  mGenerator = rb_define_module_under(mExt, "Generator");
1259
+
851
1260
  eGeneratorError = rb_path2class("JSON::GeneratorError");
852
- eCircularDatastructure = rb_path2class("JSON::CircularDatastructure");
853
1261
  eNestingError = rb_path2class("JSON::NestingError");
1262
+
854
1263
  cState = rb_define_class_under(mGenerator, "State", rb_cObject);
855
1264
  rb_define_alloc_func(cState, cState_s_allocate);
856
1265
  rb_define_singleton_method(cState, "from_state", cState_from_state_s, 1);
857
1266
  rb_define_method(cState, "initialize", cState_initialize, -1);
858
-
1267
+ rb_define_method(cState, "initialize_copy", cState_init_copy, 1);
859
1268
  rb_define_method(cState, "indent", cState_indent, 0);
860
1269
  rb_define_method(cState, "indent=", cState_indent_set, 1);
861
1270
  rb_define_method(cState, "space", cState_space, 0);
@@ -866,15 +1275,16 @@ void Init_generator()
866
1275
  rb_define_method(cState, "object_nl=", cState_object_nl_set, 1);
867
1276
  rb_define_method(cState, "array_nl", cState_array_nl, 0);
868
1277
  rb_define_method(cState, "array_nl=", cState_array_nl_set, 1);
869
- rb_define_method(cState, "check_circular?", cState_check_circular_p, 0);
870
1278
  rb_define_method(cState, "max_nesting", cState_max_nesting, 0);
871
1279
  rb_define_method(cState, "max_nesting=", cState_max_nesting_set, 1);
1280
+ rb_define_method(cState, "check_circular?", cState_check_circular_p, 0);
872
1281
  rb_define_method(cState, "allow_nan?", cState_allow_nan_p, 0);
873
- rb_define_method(cState, "seen?", cState_seen_p, 1);
874
- rb_define_method(cState, "remember", cState_remember, 1);
875
- rb_define_method(cState, "forget", cState_forget, 1);
1282
+ rb_define_method(cState, "ascii_only?", cState_ascii_only_p, 0);
876
1283
  rb_define_method(cState, "configure", cState_configure, 1);
877
1284
  rb_define_method(cState, "to_h", cState_to_h, 0);
1285
+ rb_define_method(cState, "[]", cState_aref, 1);
1286
+ rb_define_method(cState, "generate", cState_generate, 1);
1287
+ rb_define_method(cState, "partial_generate", cState_partial_generate, 1);
878
1288
 
879
1289
  mGeneratorMethods = rb_define_module_under(mGenerator, "GeneratorMethods");
880
1290
  mObject = rb_define_module_under(mGeneratorMethods, "Object");
@@ -901,6 +1311,7 @@ void Init_generator()
901
1311
  mNilClass = rb_define_module_under(mGeneratorMethods, "NilClass");
902
1312
  rb_define_method(mNilClass, "to_json", mNilClass_to_json, -1);
903
1313
 
1314
+ CRegexp_MULTILINE = rb_const_get(rb_cRegexp, rb_intern("MULTILINE"));
904
1315
  i_to_s = rb_intern("to_s");
905
1316
  i_to_json = rb_intern("to_json");
906
1317
  i_new = rb_intern("new");
@@ -909,11 +1320,22 @@ void Init_generator()
909
1320
  i_space_before = rb_intern("space_before");
910
1321
  i_object_nl = rb_intern("object_nl");
911
1322
  i_array_nl = rb_intern("array_nl");
912
- i_check_circular = rb_intern("check_circular");
913
1323
  i_max_nesting = rb_intern("max_nesting");
914
1324
  i_allow_nan = rb_intern("allow_nan");
1325
+ i_ascii_only = rb_intern("ascii_only");
915
1326
  i_pack = rb_intern("pack");
916
1327
  i_unpack = rb_intern("unpack");
917
1328
  i_create_id = rb_intern("create_id");
918
1329
  i_extend = rb_intern("extend");
1330
+ i_key_p = rb_intern("key?");
1331
+ i_aref = rb_intern("[]");
1332
+ i_send = rb_intern("__send__");
1333
+ i_respond_to_p = rb_intern("respond_to?");
1334
+ i_match = rb_intern("match");
1335
+ #ifdef HAVE_RUBY_ENCODING_H
1336
+ CEncoding_UTF_8 = rb_funcall(rb_path2class("Encoding"), rb_intern("find"), 1, rb_str_new2("utf-8"));
1337
+ i_encoding = rb_intern("encoding");
1338
+ i_encode = rb_intern("encode");
1339
+ #endif
1340
+ CJSON_SAFE_STATE_PROTOTYPE = Qnil;
919
1341
  }