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 =
{
bytes: 1,
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
}.freeze
IEC_BASE =
1024
IEC_ORDERS_OF_MAGNITUDE =
{
bytes: 1,
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
}.freeze
UNIT_SYMBOLS =
SI_ORDERS_OF_MAGNITUDE.merge(IEC_ORDERS_OF_MAGNITUDE)
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_ORDERS_OF_MAGNITUDE.keys.reject{|k| k == :bytes }.join('|') })\s*\z/i.freeze
IEC_REGEX =
/\A\s*(\-?[0-9]+(\.[0-9]+)?)\s*(#{ IEC_ORDERS_OF_MAGNITUDE.keys.reject{|k| k == :bytes }.join('|') })\s*\z/i.freeze
VERSION =
"0.1.0".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.

Raises:

  • (TypeError)


373
374
375
376
377
# File 'lib/bytesize.rb', line 373

def initialize( bytes )
	raise( TypeError, "expected #{Integer}, got #{bytes.class}" ) unless bytes.is_a?(Integer)
	@bytes = bytes
	freeze
end

Instance Attribute Details

#bytesObject Also known as: to_bytes

:stopdoc:



875
876
877
# File 'lib/bytesize.rb', line 875

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)


138
139
140
141
# File 'lib/bytesize.rb', line 138

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:



332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
# File 'lib/bytesize.rb', line 332

def self.new( val )
	if val.class == self
		super( val.bytes )
	else
		case val
			
			when Integer
			super( val )
			
			when ByteSize
			super( val.bytes )
			
			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
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.



312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
# File 'lib/bytesize.rb', line 312

def self.parse( val )
	case val
		when 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 #{str.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.



637
638
639
640
641
642
643
644
645
646
647
648
649
# File 'lib/bytesize.rb', line 637

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.



662
663
664
665
666
667
668
669
670
671
672
673
674
# File 'lib/bytesize.rb', line 662

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.



687
688
689
690
691
692
693
694
695
696
697
698
699
# File 'lib/bytesize.rb', line 687

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.



711
712
713
714
715
716
717
718
719
720
721
722
723
# File 'lib/bytesize.rb', line 711

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.



733
734
735
# File 'lib/bytesize.rb', line 733

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.



747
748
749
750
751
752
753
754
755
756
757
758
759
# File 'lib/bytesize.rb', line 747

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.



769
770
771
# File 'lib/bytesize.rb', line 769

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.



786
787
788
789
790
791
792
793
794
795
796
797
798
# File 'lib/bytesize.rb', line 786

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.



547
548
549
550
551
552
553
554
555
556
557
558
559
# File 'lib/bytesize.rb', line 547

def <( other )
	case other
		
		when Numeric
		bytes < other
		
		when ByteSize
		bytes < other.bytes
		
	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.



569
570
571
572
573
574
575
576
577
578
579
580
581
# File 'lib/bytesize.rb', line 569

def <=( other )
	case other
		
		when Numeric
		bytes <= other
		
		when ByteSize
		bytes <= other.bytes
		
	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.



812
813
814
815
816
817
818
819
820
821
822
823
824
# File 'lib/bytesize.rb', line 812

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.



836
837
838
839
840
841
842
843
844
845
846
847
848
# File 'lib/bytesize.rb', line 836

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.



591
592
593
594
595
596
597
598
599
600
601
602
603
# File 'lib/bytesize.rb', line 591

def >( other )
	case other
		
		when Numeric
		bytes > other
		
		when ByteSize
		bytes > other.bytes
		
	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.



613
614
615
616
617
618
619
620
621
622
623
624
625
# File 'lib/bytesize.rb', line 613

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

#coerce(other) ⇒ Object

:stopdoc:



881
882
883
884
885
886
887
888
889
890
891
# File 'lib/bytesize.rb', line 881

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)


902
903
904
# File 'lib/bytesize.rb', line 902

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

#hashObject

:stopdoc:



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

def hash
	bytes.hash
end

#inspectObject

:call-seq:

inspect  ->  string

Return a String describing this object.

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


926
927
928
# File 'lib/bytesize.rb', line 926

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)


938
939
940
# File 'lib/bytesize.rb', line 938

def negative?
	bytes < 0
end

#positive?Boolean

:call-seq:

positive?  ->  true or false

Returns true if the ByteSize is greater than 0.

Returns:

  • (Boolean)


950
951
952
# File 'lib/bytesize.rb', line 950

def positive?
	bytes > 0
end

#to_iObject

:call-seq:

to_i  ->  integer

Returns the number of bytes as an Integer.



976
977
978
# File 'lib/bytesize.rb', line 976

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.



990
991
992
# File 'lib/bytesize.rb', line 990

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"


1014
1015
1016
1017
1018
1019
1020
1021
1022
1023
1024
1025
1026
1027
1028
1029
1030
1031
1032
1033
1034
1035
1036
1037
1038
1039
1040
# File 'lib/bytesize.rb', line 1014

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.



1052
1053
1054
# File 'lib/bytesize.rb', line 1052

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)


1064
1065
1066
# File 'lib/bytesize.rb', line 1064

def zero?
	bytes == 0
end