1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211
|
require 'digest'
module Digest
#
# Base class for all CRC algorithms.
#
class CRC < Digest::Class
include Digest::Instance
# The initial value of the CRC checksum
INIT_CRC = 0x00
# The XOR mask to apply to the resulting CRC checksum
XOR_MASK = 0x00
# The bit width of the CRC checksum
WIDTH = 0
# Define true or false whether the input direction is bit reversed or not of the CRC checksum
REFLECT_INPUT = nil
# Default place holder CRC table
TABLE = [].freeze
#
# Calculates the CRC checksum.
#
# @param [String] data
# The given data.
#
# @return [Integer]
# The CRC checksum.
#
def self.checksum(data)
crc = self.new
crc << data
return crc.checksum
end
#
# Packs the given CRC checksum.
#
# @param [Integer] crc
# The raw CRC checksum.
#
# @return [String]
# The packed CRC checksum.
#
def self.pack(crc)
unless (width = self::WIDTH) > 0
raise(NotImplementedError, "#{self} is incompleted as CRC")
end
bitclass = width + (-width & 0x07)
byte_count = bitclass / 8
crc &= ~(-1 << width)
result = [crc].pack("Q>")
result[0, result.bytesize - byte_count] = ""
result
end
#
# Determines whether the library is using the optimized C extensions
# implementation, or the pure-Ruby implementation.
#
# @return [:c_ext, :pure]
#
# @since 0.7.0
#
def self.implementation
if instance_method(:update).source_location.nil?
:c_ext
else
:pure
end
end
#
# Initializes the CRC checksum.
#
def initialize
@init_crc = self.class.const_get(:INIT_CRC)
@xor_mask = self.class.const_get(:XOR_MASK)
@width = self.class.const_get(:WIDTH)
@reflect_input = self.class.const_get(:REFLECT_INPUT)
@table = self.class.const_get(:TABLE)
reset
end
#
# The input block length.
#
# @return [1]
#
def block_length
1
end
#
# The length of the digest.
#
# @return [Integer]
# The length in bytes.
#
def digest_length
(@width / 8.0).ceil
end
#
# Updates the CRC checksum with the given data.
#
# @param [String] data
# The data to update the CRC checksum with.
#
# @raise [NotImplementedError]
# If WIDTH, TABLE, or REFLECT_INPUT constants are not set properly.
#
def update(data)
unless @width >= 1
raise(NotImplementedError, "incompleted #{self.class} as CRC (expected WIDTH to be 1 or more)")
end
if @table.empty?
raise(NotImplementedError, "incompleted #{self.class} as CRC (expected TABLE to be not empty)")
end
if @reflect_input.nil?
raise(NotImplementedError, "incompleted #{self.class} as CRC (expected REFLECT_INPUT to be not nil)")
end
table = @table
crc = @crc
if @reflect_input
if @width > 8
data.each_byte do |b|
crc = table[b ^ (0xff & crc)] ^ (crc >> 8)
end
else
data.each_byte do |b|
# Omit (crc >> 8) since bits upper than the lower 8 bits are always 0
crc = table[b ^ (0xff & crc)]
end
end
else
if @width > 8
higher_bit_off = @width - 8
remain_mask = ~(-1 << higher_bit_off)
data.each_byte do |b|
crc = table[b ^ (0xff & (crc >> higher_bit_off))] ^ ((remain_mask & crc) << 8)
end
else
padding = 8 - @width
data.each_byte do |b|
# Omit (crc << 8) since bits lower than the upper 8 bits are always 0
crc = table[b ^ (0xff & (crc << padding))]
end
end
end
@crc = crc
self
end
#
# @see #update
#
def <<(data)
update(data)
return self
end
#
# Resets the CRC checksum.
#
# @return [Integer]
# The default value of the CRC checksum.
#
def reset
@crc = @init_crc
end
#
# The resulting CRC checksum.
#
# @return [Integer]
# The resulting CRC checksum.
#
def checksum
@crc ^ @xor_mask
end
#
# Finishes the CRC checksum calculation.
#
# @see pack
#
def finish
self.class.pack(checksum)
end
end
end
|