Class: ByteSize

Inherits:
Object
  • Object
show all
Includes:
Comparable
Defined in:
lib/bytesize.rb,
lib/bytesize/version.rb,
lib/bytesize/activerecord.rb

Overview

This class is used to represent a size in bytes.

It uses the SI standard unit symbols: kB, MB, GB, TB, PB, EB, ZB, YB.

For a version that uses the IEC standard unit symbols, see IECByteSize[IECByteSize.html].

Examples of use

ByteSize.new( 4127 )	  #=> (4.13 kB)
ByteSize.new( "22 GB" )   #=> (22 GB)
ByteSize.new( "22 GiB" )  #=> (23.62 GB)

ByteSize.bytes( 42 )	  #=> (42 bytes)

ByteSize.kb( 42 )		 #=> (42 kB)
ByteSize.mb( 42 )		 #=> (42 MB)
ByteSize.gb( 42 )		 #=> (42 GB)
ByteSize.tb( 42 )		 #=> (42 TB)
ByteSize.pb( 42 )		 #=> (42 PB)
ByteSize.eb( 42 )		 #=> (42 EB)
ByteSize.zb( 42 )		 #=> (42 ZB)
ByteSize.yb( 42 )		 #=> (42 YB)

Conversion of values:

ByteSize.gb( 100 ).to_gib	#=> 93.13225746154785

ByteSize.gib( 2.42 ).to_mib  #=> 2478.079999923706

With numeric convenience methods:

require 'bytesize/unit'

100.gb.to_gib	#=> 93.13225746154785

2.42.gib.to_mib  #=> 2478.079999923706

Direct Known Subclasses

IECByteSize

Defined Under Namespace

Classes: ActiveRecordType

Constant Summary collapse

SI_BASE =

:stopdoc:

1000
SI_ORDERS_OF_MAGNITUDE =
{
kB:	SI_BASE,
MB:	SI_BASE**2,
GB:	SI_BASE**3,
TB:	SI_BASE**4,
PB:	SI_BASE**5,
EB:	SI_BASE**6,
ZB:	SI_BASE**7,
YB:	SI_BASE**8
}
SI_UNIT_SYMBOLS =
SI_ORDERS_OF_MAGNITUDE.keys.freeze
IEC_BASE =
1024
IEC_ORDERS_OF_MAGNITUDE =
{
KiB:   IEC_BASE,
MiB:   IEC_BASE**2,
GiB:   IEC_BASE**3,
TiB:   IEC_BASE**4,
PiB:   IEC_BASE**5,
EiB:   IEC_BASE**6,
ZiB:   IEC_BASE**7,
YiB:   IEC_BASE**8
}
IEC_UNIT_SYMBOLS =
IEC_ORDERS_OF_MAGNITUDE.keys.freeze
ALL_ORDERS_OF_MAGNITUDE =
SI_ORDERS_OF_MAGNITUDE.merge(IEC_ORDERS_OF_MAGNITUDE).freeze
ALL_UNIT_SYMBOLS =
ALL_ORDERS_OF_MAGNITUDE.keys.freeze
BASE =
SI_BASE
UNIT_SYMBOLS =
SI_UNIT_SYMBOLS
ORDERS_OF_MAGNITUDE =
SI_ORDERS_OF_MAGNITUDE
BYTES_REGEX =

:stopdoc:

/\A\s*(\-?[0-9]+)\s*(bytes)?\s*\z/.freeze
SI_REGEX =
/\A\s*(\-?[0-9]+(\.[0-9]+)?)\s*(#{ SI_UNIT_SYMBOLS.join('|') })\s*\z/i.freeze
IEC_REGEX =
/\A\s*(\-?[0-9]+(\.[0-9]+)?)\s*(#{ IEC_UNIT_SYMBOLS.join('|') })\s*\z/i.freeze
VERSION =
'0.1.2'.freeze

Instance Attribute Summary collapse

Class Method Summary collapse

Instance Method Summary collapse

Constructor Details

#initialize(bytes) ⇒ ByteSize

call-seq:

new( integer )
new( string )

Create a new instance of ByteSize from an Integer or String.



381
382
383
# File 'lib/bytesize.rb', line 381

def initialize( bytes )
	@bytes = bytes
end

Instance Attribute Details

#bytesObject Also known as: to_bytes

:stopdoc:



881
882
883
# File 'lib/bytesize.rb', line 881

def bytes
  @bytes
end

Class Method Details

.bytes(b) ⇒ Object

:call-seq:

ByteSize.bytes( n )  ->  bytesize

Returns a new instance of ByteSize representing n bytes.

Raises:

  • (TypeError)


152
153
154
155
# File 'lib/bytesize.rb', line 152

def self.bytes( b )
	raise( TypeError, "expected #{Numeric}, got #{b.class}" ) unless b.is_a?(Numeric)
	self.new( b.round )
end

.new(val) ⇒ Object

:stopdoc:



345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
# File 'lib/bytesize.rb', line 345

def self.new( val )
	case val
			
		when ByteSize
		super( val.bytes )
		
		when Integer
		super( val )
		
		when String
		if m = val.match(BYTES_REGEX)
			super( m[1].to_i )
		elsif m = val.match(SI_REGEX)
			self.send( m[3].downcase.to_sym, m[2].nil? ? m[1].to_i : m[1].to_f )
		elsif m = val.match(IEC_REGEX)
			self.send( m[3].downcase.to_sym, m[2].nil? ? m[1].to_i : m[1].to_f )
		else
			raise( ArgumentError, "invalid #{self} string: #{val.inspect}" )
		end
		
	else
		raise( TypeError, "no implicit conversion of #{val.class} into #{self}" )	
	end
end

.parse(val) ⇒ Object

call-seq:

ByteSize.parse( string )  ->  bytesize

Parses a String into either a ByteSize or IECByteSize depending on it’s unit symbol.



326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
# File 'lib/bytesize.rb', line 326

def self.parse( val )
	if val.is_a?(String)
		if m = val.match(BYTES_REGEX)
			ByteSize.new( m[1].to_i )
		elsif m = val.match(SI_REGEX)
			ByteSize.send( m[3].downcase.to_sym, m[2].nil? ? m[1].to_i : m[1].to_f )
		elsif m = val.match(IEC_REGEX)
			IECByteSize.send( m[3].downcase.to_sym, m[2].nil? ? m[1].to_i : m[1].to_f )
		else
			raise( ArgumentError, "invalid #{ByteSize} or #{IECByteSize} string: #{val.inspect}" )
		end
	else
		raise( TypeError, "expected #{String}, got #{val.class}" )
	end
end

Instance Method Details

#%(val) ⇒ Object

:call-seq:

bytesize % val  ->  bytesize

Performs a modulo operation with val, returning an instance of ByteSize.

val can be another ByteSize or a Numeric.



643
644
645
646
647
648
649
650
651
652
653
654
655
# File 'lib/bytesize.rb', line 643

def %( val )
	case val
		
		when Numeric
		self.class.new(( bytes % val ).round)
		
		when ByteSize
		self.class.new( bytes % val.bytes )
		
	else 
		raise( TypeError, "#{val.class} can't be coerced into #{self.class}" )
	end
end

#*(val) ⇒ Object

:call-seq:

bytesize * val  ->  bytesize

Performs multiplication, returning an instance of ByteSize.

val must be a Numeric. Multiplication with another ByteSize is disallowed because it does not make symantic sense.



668
669
670
671
672
673
674
675
676
677
678
679
680
# File 'lib/bytesize.rb', line 668

def *( val )
	case val
		
		when Numeric
		self.class.new(( bytes * val ).round)
		
		when ByteSize
		raise( TypeError, "cannot multiply #{ByteSize} with #{val.class}" )
		
	else 
		raise( TypeError, "#{val.class} can't be coerced into #{self.class}" )
	end
end

#**(pow) ⇒ Object

:call-seq:

bytesize ** pow  ->  bytesize

Raises bytesize to the power of pow, returning an instance of ByteSize.

pow must be a Numeric. Raising to the power of another ByteSize is disallowed because it does not make symantic sense.



693
694
695
696
697
698
699
700
701
702
703
704
705
# File 'lib/bytesize.rb', line 693

def **( pow )
	case pow
		
		when Numeric
		self.class.new(( bytes ** pow ).round)
		
		when ByteSize
		raise( TypeError, "cannot raise #{ByteSize} to a power of #{pow.class}" )
		
	else 
		raise( TypeError, "#{pow.class} can't be coerced into #{self.class}" )
	end
end

#+(val) ⇒ Object

:call-seq:

bytesize + val  ->  bytesize

Performs addition, returning an instance of ByteSize.

val can be another ByteSize or a Numeric.



717
718
719
720
721
722
723
724
725
726
727
728
729
# File 'lib/bytesize.rb', line 717

def +( val )
	case val
		
		when Numeric
		self.class.new(( bytes + val ).round)
		
		when ByteSize
		self.class.new( bytes + val.bytes )
		
	else 
		raise( TypeError, "#{val.class} can't be coerced into #{self.class}" )
	end
end

#+@Object

:call-seq:

+bytesize  ->  bytesize

Unary Plus — Returns the receiver’s value.



739
740
741
# File 'lib/bytesize.rb', line 739

def +@
	self
end

#-(val) ⇒ Object

:call-seq:

bytesize - val  ->  bytesize

Performs subtraction, returning an instance of ByteSize.

val can be another ByteSize or a Numeric.



753
754
755
756
757
758
759
760
761
762
763
764
765
# File 'lib/bytesize.rb', line 753

def -( val )
	case val
		
		when Numeric
		self.class.new(( bytes - val ).round)
		
		when ByteSize
		self.class.new( bytes - val.bytes )
		
	else 
		raise( TypeError, "#{val.class} can't be coerced into #{self.class}" )
	end
end

#-@Object

:call-seq:

-bytesize  ->  bytesize

Unary Minus — Returns the receiver’s value, negated.



775
776
777
# File 'lib/bytesize.rb', line 775

def -@
	self.class.new( 0 - bytes )
end

#/(val) ⇒ Object

:call-seq:

bytesize / val  ->  bytesize or float

Performs division.

If val is a Numeric it returns an instance of ByteSize. If val is an instance of ByteSize it returns a Float.



792
793
794
795
796
797
798
799
800
801
802
803
804
# File 'lib/bytesize.rb', line 792

def /( val )
	case val
		
		when Numeric
		self.class.new(( bytes / val ).round)
		
		when ByteSize
		bytes.to_f / val.bytes.to_f
		
	else 
		raise( TypeError, "#{val.class} can't be coerced into #{self.class}" )
	end
end

#<(other) ⇒ Object

:call-seq:

bytesize < val  ->  true or false

Returns true if the value of bytesize is less than that of val.



553
554
555
556
557
558
559
560
561
562
563
564
565
# File 'lib/bytesize.rb', line 553

def <( other )
	case other
		
		when ByteSize
		bytes < other.bytes
		
		when Numeric
		bytes < other
		
	else 
		raise( ArgumentError, "comparison of #{self.class} with #{other.inspect} failed" )
	end
end

#<=(other) ⇒ Object

:call-seq:

bytesize <= val  ->  true or false

Returns true if the value of bytesize is less than or equal to that of val.



575
576
577
578
579
580
581
582
583
584
585
586
587
# File 'lib/bytesize.rb', line 575

def <=( other )
	case other
		
		when ByteSize
		bytes <= other.bytes
		
		when Numeric
		bytes <= other
		
	else 
		raise( ArgumentError, "comparison of #{self.class} with #{other.inspect} failed" )
	end
end

#<=>(other) ⇒ Object

:call-seq:

bytesize <=> other  ->  -1, 0, 1, or nil

Compares bytesize to other and returns 0 if they are equal, -1 if bytesize is less than other, or 1 if bytesize is greater than other.

Returns nil if the two values are incomparable.



818
819
820
821
822
823
824
825
826
827
828
829
830
# File 'lib/bytesize.rb', line 818

def <=>( other )
	case other
		
		when Numeric
		bytes <=> other
		
		when ByteSize
		bytes <=> other.bytes
		
	else 
		nil
	end
end

#==(other) ⇒ Object Also known as: ===

:call-seq:

bytesize == other  ->  true or false

Returns true if bytesize is equal to other.

If other is not an instance of ByteSize an attempt will be made to convert it to one.



842
843
844
845
846
847
848
849
850
851
852
853
854
# File 'lib/bytesize.rb', line 842

def ==( other )
	case other
		
		when Numeric
		bytes == other
		
		when ByteSize
		bytes == other.bytes
		
	else 
		false
	end
end

#>(other) ⇒ Object

:call-seq:

bytesize > val  ->  true or false

Returns true if the value of bytesize is greater than that of val.



597
598
599
600
601
602
603
604
605
606
607
608
609
# File 'lib/bytesize.rb', line 597

def >( other )
	case other
		
		when ByteSize
		bytes > other.bytes
		
		when Numeric
		bytes > other
		
	else 
		raise( ArgumentError, "comparison of #{self.class} with #{other.inspect} failed" )
	end
end

#>=(other) ⇒ Object

:call-seq:

bytesize >= val  ->  true or false

Returns true if the value of bytesize is greater than or equal to that of val.



619
620
621
622
623
624
625
626
627
628
629
630
631
# File 'lib/bytesize.rb', line 619

def >=( other )
	case other
		
		when ByteSize
		bytes >= other.bytes
		
		when Numeric
		bytes >= other
		
	else 
		raise( ArgumentError, "comparison of #{self.class} with #{other.inspect} failed" )
	end
end

#coerce(other) ⇒ Object

:stopdoc:



887
888
889
890
891
892
893
894
895
896
897
# File 'lib/bytesize.rb', line 887

def coerce( other )
	if Numeric
		[ other, self.to_i ]
	else
		begin
			[ self.class.new(other), self ]
		rescue
			raise( TypeError, "#{other.class} can't be coerced into #{self.class}" )
		end
	end
end

#eql?(other) ⇒ Boolean

:call-seq:

eql?( other_bytesize )  ->  true or false

Returns true if the ByteSize is equal to other_bytesize.

Returns:

  • (Boolean)


908
909
910
# File 'lib/bytesize.rb', line 908

def eql?( other )
	other.class == self.class && other.bytes == bytes
end

#hashObject

:stopdoc:



915
916
917
# File 'lib/bytesize.rb', line 915

def hash
	bytes.hash
end

#inspectObject

:call-seq:

inspect  ->  string

Return a String describing this object.

Example:
ByteSize.bytes(3000000000000)  #=> (3 TB)


932
933
934
# File 'lib/bytesize.rb', line 932

def inspect
	sprintf( '(%s)', to_s )
end

#negative?Boolean

:call-seq:

negative?  ->  true or false

Returns true if the ByteSize is less than 0.

Returns:

  • (Boolean)


944
945
946
# File 'lib/bytesize.rb', line 944

def negative?
	bytes < 0
end

#positive?Boolean

:call-seq:

positive?  ->  true or false

Returns true if the ByteSize is greater than 0.

Returns:

  • (Boolean)


956
957
958
# File 'lib/bytesize.rb', line 956

def positive?
	bytes > 0
end

#to_iObject

:call-seq:

to_i  ->  integer

Returns the number of bytes as an Integer.



982
983
984
# File 'lib/bytesize.rb', line 982

def to_i
	bytes.to_i
end

#to_iecObject

:call-seq:

to_iec  ->  iecbytesize

Returns the size as an instance of IECByteSize.

If called on an instance of IECByteSize it returns self.



996
997
998
# File 'lib/bytesize.rb', line 996

def to_iec
	self.class == IECByteSize ? self : IECByteSize.new(self.to_i)
end

#to_s(decimal_places = nil) ⇒ Object

:call-seq:

to_s  ->  string
to_s( decimal_places )  ->  string

Format this ByteSize as a String.

The second form formats it with exactly decimal_places decimal places.

Example:
ByteSize.bytes(3000000000000).to_s	 #=> "3 TB"
ByteSize.bytes(2460000000000).to_s	 #=> "2.46 TB"

ByteSize.bytes(3000000000000).to_s(2)  #=> "3.00 TB"
ByteSize.bytes(1234567890000).to_s(2)  #=> "1.23 TB"
ByteSize.bytes(1234567890000).to_s(4)  #=> "1.2346 TB"


1020
1021
1022
1023
1024
1025
1026
1027
1028
1029
1030
1031
1032
1033
1034
1035
1036
1037
1038
1039
1040
1041
1042
1043
1044
1045
1046
# File 'lib/bytesize.rb', line 1020

def to_s( decimal_places=nil )
	unless decimal_places.nil?
		raise( TypeError, "expected #{Integer}, got #{decimal_places.class}" ) unless decimal_places.is_a?(Integer)
		raise( RangeError, "decimal places cannot be negative" ) unless decimal_places >= 0
	end
	
	b = bytes.abs
	
	scale = self.class::ORDERS_OF_MAGNITUDE.sort{|(ak,av),(bk,bv)| av <=> bv }
	
	if b == 0
		unit = scale.first
	else
		unit = scale.find_index{|k,v| v > b }
		if unit.nil?
			unit = scale.last
		else
			unit = scale[unit-1]
		end
	end
	
	if decimal_places.nil?
		sprintf( negative? ? '-%g %s' : '%g %s', ((b/Float(unit.last))*100).round/100.0, unit.first.to_s )
	else
		sprintf( negative? ? "-%.0#{decimal_places}f %s" : "%.0#{decimal_places}f %s", b / Float(unit.last), unit.first.to_s )
	end
end

#to_siObject

:call-seq:

to_si  ->  bytesize

Returns the size as an instance of ByteSize.

If called on an instance of ByteSize it returns self.



1058
1059
1060
# File 'lib/bytesize.rb', line 1058

def to_si
	self.class == ByteSize ? self : ByteSize.new(self.to_i)
end

#zero?Boolean

:call-seq:

zero?  ->  true or false

Returns true if the ByteSize has a zero value.

Returns:

  • (Boolean)


1070
1071
1072
# File 'lib/bytesize.rb', line 1070

def zero?
	bytes == 0
end