fast-blank-1.0.0/0000755000175000017500000000000013250777716012575 5ustar pravipravifast-blank-1.0.0/README.md0000644000175000017500000000556113250777716014063 0ustar pravipravi### String blank? Ruby Extension [![Gem Version](https://badge.fury.io/rb/fast_blank.png)](http://badge.fury.io/rb/fast_blank) [![Build Status](https://travis-ci.org/SamSaffron/fast_blank.png?branch=master)](https://travis-ci.org/SamSaffron/fast_blank) `fast_blank` is a simple extension which provides a fast implementation of active support's string#blank? function ### How do you use it? require 'fast_blank' ### How fast is "Fast"? About 5-9x faster than current active support, on my machine (your mileage my vary): $ ./benchmark ``` user system total real user system total real Fast Blank 0 : 0.070000 0.000000 0.070000 ( 0.075247) Fast Blank (Active Support) 0 : 0.080000 0.000000 0.080000 ( 0.075029) Slow Blank 0 : 0.500000 0.000000 0.500000 ( 0.503026) Fast Blank 6 : 0.200000 0.000000 0.200000 ( 0.191480) Fast Blank (Active Support) 6 : 0.180000 0.000000 0.180000 ( 0.179891) Slow Blank 6 : 0.660000 0.000000 0.660000 ( 0.658604) Fast Blank 14 : 0.080000 0.010000 0.090000 ( 0.086371) Fast Blank (Active Support) 14 : 0.130000 0.000000 0.130000 ( 0.129258) Slow Blank 14 : 0.890000 0.000000 0.890000 ( 0.886140) Fast Blank 24 : 0.150000 0.000000 0.150000 ( 0.158151) Fast Blank (Active Support) 24 : 0.140000 0.000000 0.140000 ( 0.149284) Slow Blank 24 : 0.900000 0.000000 0.900000 ( 0.899663) Fast Blank 136 : 0.130000 0.000000 0.130000 ( 0.125831) Fast Blank (Active Support) 136 : 0.150000 0.000000 0.150000 ( 0.148948) Slow Blank 136 : 0.900000 0.000000 0.900000 ( 0.899885) ``` Additionally, this gem allocates no strings during the test, making it less of a GC burden. ###Compatibility note: fast_blank is supported under MRI Ruby 1.9.3, 2.0 and 2.1, earlier versions of MRI are untested. fast_blank implements string.blank? as MRI would have it implemented, meaning it has 100% parity with `String#strip.length == 0`. Active Supports version looks also at unicode spaces for example: `"\u2000\u2001\u2002\u2003\u2004\u2005\u2006\u2007\u2008\u2009\u200A\u202F\u205F\u3000".blank?` is true in Active Support even though fast_blank would treat it as not blank. fast_blank also provides blank_as? which is a 100% compatible blank? replacement. Author: Sam Saffron sam.saffron@gmail.com http://github.com/SamSaffron/fast_blank License: MIT ### Change log: 0.0.2: - Removed rake dependency (tmm1) - Unrolled internal loop to improve perf (tmm1) (gem template based on https://github.com/CodeMonkeySteve/fast_xor ) fast-blank-1.0.0/MIT-LICENSE0000644000175000017500000000204513250777716014232 0ustar pravipraviCopyright (c) 2006-2009 Steve Sloan Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. fast-blank-1.0.0/fast_blank.gemspec0000644000175000017500000000331213250777716016245 0ustar pravipravi######################################################### # This file has been automatically generated by gem2tgz # ######################################################### # -*- encoding: utf-8 -*- # stub: fast_blank 1.0.0 ruby lib # stub: ext/fast_blank/extconf.rb Gem::Specification.new do |s| s.name = "fast_blank".freeze s.version = "1.0.0" s.required_rubygems_version = Gem::Requirement.new(">= 0".freeze) if s.respond_to? :required_rubygems_version= s.require_paths = ["lib".freeze] s.authors = ["Sam Saffron".freeze] s.date = "2015-08-03" s.description = "Provides a C-optimized method for determining if a string is blank".freeze s.email = "sam.saffron@gmail.com".freeze s.extensions = ["ext/fast_blank/extconf.rb".freeze] s.files = ["MIT-LICENSE".freeze, "README.md".freeze, "benchmark".freeze, "ext/fast_blank/extconf.rb".freeze, "ext/fast_blank/fast_blank.c".freeze, "lib/.gemkeep".freeze, "spec/fast_blank_spec.rb".freeze] s.homepage = "https://github.com/SamSaffron/fast_blank".freeze s.licenses = ["MIT".freeze] s.rubygems_version = "2.7.6".freeze s.summary = "Fast String blank? implementation".freeze s.test_files = ["spec/fast_blank_spec.rb".freeze] if s.respond_to? :specification_version then s.specification_version = 4 if Gem::Version.new(Gem::VERSION) >= Gem::Version.new('1.2.0') then s.add_development_dependency(%q.freeze, [">= 0"]) s.add_development_dependency(%q.freeze, [">= 0"]) else s.add_dependency(%q.freeze, [">= 0"]) s.add_dependency(%q.freeze, [">= 0"]) end else s.add_dependency(%q.freeze, [">= 0"]) s.add_dependency(%q.freeze, [">= 0"]) end end fast-blank-1.0.0/lib/0000755000175000017500000000000013250777716013343 5ustar pravipravifast-blank-1.0.0/lib/.gemkeep0000644000175000017500000000000013250777716014747 0ustar pravipravifast-blank-1.0.0/ext/0000755000175000017500000000000013250777716013375 5ustar pravipravifast-blank-1.0.0/ext/fast_blank/0000755000175000017500000000000013250777716015501 5ustar pravipravifast-blank-1.0.0/ext/fast_blank/extconf.rb0000644000175000017500000000005413250777716017473 0ustar pravipravirequire 'mkmf' create_makefile 'fast_blank' fast-blank-1.0.0/ext/fast_blank/fast_blank.c0000644000175000017500000000400013250777716017743 0ustar pravipravi#include #include #include #include #include #define STR_ENC_GET(str) rb_enc_from_index(ENCODING_GET(str)) /* Backward compatibility with Ruby 1.8 */ #ifndef RSTRING_PTR #define RSTRING_PTR(s) (RSTRING(s)->ptr) #endif #ifndef RSTRING_LEN #define RSTRING_LEN(s) (RSTRING(s)->len) #endif static int ruby_version_before_2_2() { #ifdef RUBY_API_VERSION_MAJOR if (RUBY_API_VERSION_MAJOR > 2 || (RUBY_API_VERSION_MAJOR == 2 && RUBY_API_VERSION_MINOR >= 2)) { return 0; } #endif return 1; } static VALUE rb_str_blank_as(VALUE str) { rb_encoding *enc; char *s, *e; enc = STR_ENC_GET(str); s = RSTRING_PTR(str); if (!s || RSTRING_LEN(str) == 0) return Qtrue; e = RSTRING_END(str); while (s < e) { int n; unsigned int cc = rb_enc_codepoint_len(s, e, &n, enc); switch (cc) { case 9: case 0xa: case 0xb: case 0xc: case 0xd: case 0x20: case 0x85: case 0xa0: case 0x1680: case 0x2000: case 0x2001: case 0x2002: case 0x2003: case 0x2004: case 0x2005: case 0x2006: case 0x2007: case 0x2008: case 0x2009: case 0x200a: case 0x2028: case 0x2029: case 0x202f: case 0x205f: case 0x3000: /* found */ break; case 0x180e: if (ruby_version_before_2_2()) break; default: return Qfalse; } s += n; } return Qtrue; } static VALUE rb_str_blank(VALUE str) { rb_encoding *enc; char *s, *e; enc = STR_ENC_GET(str); s = RSTRING_PTR(str); if (!s || RSTRING_LEN(str) == 0) return Qtrue; e = RSTRING_END(str); while (s < e) { int n; unsigned int cc = rb_enc_codepoint_len(s, e, &n, enc); if (!rb_isspace(cc) && cc != 0) return Qfalse; s += n; } return Qtrue; } void Init_fast_blank( void ) { rb_define_method(rb_cString, "blank?", rb_str_blank, 0); rb_define_method(rb_cString, "blank_as?", rb_str_blank_as, 0); } fast-blank-1.0.0/benchmark0000755000175000017500000000435313250777716014462 0ustar pravipravi#!/usr/bin/env ruby $: << File.dirname(__FILE__)+'/lib' require 'benchmark' require 'fast_blank' class String # active support implementation def slow_blank? /\A[[:space:]]*\z/ === self end end n = 1000000 strings = [ "", "\r\n\r\n ", "this is a test", " this is a longer test", " this is a longer test this is a longer test this is a longer test this is a longer test this is a longer test" ] strings.each do |s| raise "failed on #{s.inspect}" if s.blank? != s.slow_blank? end Benchmark.bmbm do |x| strings.each do |s| x.report("Fast Blank #{s.length} :") do n.times { s.blank? } end x.report("Fast Blank (Active Support) #{s.length} :") do n.times { s.blank_as? } end x.report("Slow Blank #{s.length} :") do n.times { s.slow_blank? } end #x.report("Empty #{s.length} :") do n.times { s.empty? } end end end # user system total real # Fast Blank 0 : 0.050000 0.000000 0.050000 ( 0.048928) # Fast Blank (Active Support) 0 : 0.050000 0.000000 0.050000 ( 0.049967) # Slow Blank 0 : 0.290000 0.000000 0.290000 ( 0.295086) # Fast Blank 6 : 0.090000 0.000000 0.090000 ( 0.089295) # Fast Blank (Active Support) 6 : 0.090000 0.000000 0.090000 ( 0.089648) # Slow Blank 6 : 0.420000 0.000000 0.420000 ( 0.418375) # Fast Blank 14 : 0.060000 0.000000 0.060000 ( 0.056642) # Fast Blank (Active Support) 14 : 0.060000 0.000000 0.060000 ( 0.061443) # Slow Blank 14 : 0.670000 0.000000 0.670000 ( 0.668229) # Fast Blank 24 : 0.070000 0.000000 0.070000 ( 0.075878) # Fast Blank (Active Support) 24 : 0.090000 0.000000 0.090000 ( 0.088610) # Slow Blank 24 : 0.650000 0.000000 0.650000 ( 0.650736) # Fast Blank 136 : 0.070000 0.000000 0.070000 ( 0.075540) # Fast Blank (Active Support) 136 : 0.090000 0.000000 0.090000 ( 0.087240) # Slow Blank 136 : 0.660000 0.000000 0.660000 ( 0.655168) fast-blank-1.0.0/spec/0000755000175000017500000000000013250777716013527 5ustar pravipravifast-blank-1.0.0/spec/fast_blank_spec.rb0000644000175000017500000000216413250777716017175 0ustar pravipravirequire 'fast_blank' class ::String def blank2? /\A[[:space:]]*\z/ === self end end describe String do it "works" do expect("".blank?).to eq(true) expect(" ".blank?).to eq(true) expect("\r\n".blank?).to eq(true) "\r\n\v\f\r\s\u0085".blank? == true end it "provides a parity with active support function" do (16*16*16*16).times do |i| c = i.chr('UTF-8') rescue nil unless c.nil? expect("#{i.to_s(16)} #{c.blank_as?}").to eq("#{i.to_s(16)} #{c.blank2?}") end end (256).times do |i| c = i.chr('ASCII') rescue nil unless c.nil? expect("#{i.to_s(16)} #{c.blank_as?}").to eq("#{i.to_s(16)} #{c.blank2?}") end end end it "has parity with strip.length" do (256).times do |i| c = i.chr('ASCII') rescue nil unless c.nil? expect("#{i.to_s(16)} #{c.strip.length == 0}").to eq("#{i.to_s(16)} #{c.blank?}") end end end it "treats \u0000 correctly" do # odd I know expect("\u0000".strip.length).to eq(0) expect("\u0000".blank_as?).to be_falsey expect("\u0000".blank?).to be_truthy end end