scout 5.1.2 → 5.1.3

Sign up to get free protection for your applications and to get access to all the features.
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
  }