| 1 | # crc16.tcl -- Copyright (C) 2002 Pat Thoyts <patthoyts@users.sourceforge.net> |
|---|
| 2 | # |
|---|
| 3 | # Cyclic Redundancy Check - this is a Tcl implementation of a general |
|---|
| 4 | # table-driven CRC implementation. This code should be able to generate |
|---|
| 5 | # the lookup table and implement the correct algorithm for most types |
|---|
| 6 | # of CRC. CRC-16, CRC-32 and the CCITT version of CRC-16. [1][2][3] |
|---|
| 7 | # Most transmission CRCs use the CCITT polynomial (including X.25, SDLC |
|---|
| 8 | # and Kermit). |
|---|
| 9 | # |
|---|
| 10 | # [1] http://www.microconsultants.com/tips/crc/crc.txt for the reference |
|---|
| 11 | # implementation |
|---|
| 12 | # [2] http://www.embedded.com/internet/0001/0001connect.htm |
|---|
| 13 | # for another good discussion of why things are the way they are. |
|---|
| 14 | # [3] "Numerical Recipes in C", Press WH et al. Chapter 20. |
|---|
| 15 | # |
|---|
| 16 | # Checks: a crc for the string "123456789" should give: |
|---|
| 17 | # CRC16: 0xBB3D |
|---|
| 18 | # CRC-CCITT: 0x29B1 |
|---|
| 19 | # XMODEM: 0x31C3 |
|---|
| 20 | # CRC-32: 0xCBF43926 |
|---|
| 21 | # |
|---|
| 22 | # eg: crc::crc16 "123456789" |
|---|
| 23 | # crc::crc-ccitt "123456789" |
|---|
| 24 | # or crc::crc16 -file tclsh.exe |
|---|
| 25 | # |
|---|
| 26 | # Note: |
|---|
| 27 | # The CCITT CRC can very easily be checked for the accuracy of transmission |
|---|
| 28 | # as the CRC of the message plus the CRC values will be 0. That is: |
|---|
| 29 | # % set msg {123456789] |
|---|
| 30 | # % set crc [crc::crc-ccitt $msg] |
|---|
| 31 | # % crc::crc-ccitt $msg[binary format S $crc] |
|---|
| 32 | # 0 |
|---|
| 33 | # |
|---|
| 34 | # The same is true of other CRCs but some operate in reverse bit order: |
|---|
| 35 | # % crc::crc16 $msg[binary format s [crc::crc16 $msg]] |
|---|
| 36 | # 0 |
|---|
| 37 | # |
|---|
| 38 | # ------------------------------------------------------------------------- |
|---|
| 39 | # See the file "license.terms" for information on usage and redistribution |
|---|
| 40 | # of this file, and for a DISCLAIMER OF ALL WARRANTIES. |
|---|
| 41 | # ------------------------------------------------------------------------- |
|---|
| 42 | # $Id: crc16.tcl,v 1.15 2006/04/20 10:19:51 patthoyts Exp $ |
|---|
| 43 | |
|---|
| 44 | # @mdgen EXCLUDE: crcc.tcl |
|---|
| 45 | |
|---|
| 46 | package require Tcl 8.2; # tcl minimum version |
|---|
| 47 | |
|---|
| 48 | namespace eval ::crc { |
|---|
| 49 | |
|---|
| 50 | namespace export crc16 crc-ccitt crc-32 |
|---|
| 51 | |
|---|
| 52 | variable crc16_version 1.1.1 |
|---|
| 53 | |
|---|
| 54 | # Standard CRC generator polynomials. |
|---|
| 55 | variable polynomial |
|---|
| 56 | set polynomial(crc16) [expr {(1<<16) | (1<<15) | (1<<2) | 1}] |
|---|
| 57 | set polynomial(ccitt) [expr {(1<<16) | (1<<12) | (1<<5) | 1}] |
|---|
| 58 | set polynomial(crc32) [expr {(1<<32) | (1<<26) | (1<<23) | (1<<22) |
|---|
| 59 | | (1<<16) | (1<<12) | (1<<11) | (1<<10) |
|---|
| 60 | | (1<<8) | (1<<7) | (1<<5) | (1<<4) |
|---|
| 61 | | (1<<2) | (1<<1) | 1}] |
|---|
| 62 | |
|---|
| 63 | # Array to hold the generated tables |
|---|
| 64 | variable table |
|---|
| 65 | if {![info exists table]} { array set table {}} |
|---|
| 66 | |
|---|
| 67 | # calculate the sign bit for the current platform. |
|---|
| 68 | variable signbit |
|---|
| 69 | if {![info exists signbit]} { |
|---|
| 70 | variable v |
|---|
| 71 | for {set v 1} {int($v) != 0} {set signbit $v; set v [expr {$v<<1}]} {} |
|---|
| 72 | unset v |
|---|
| 73 | } |
|---|
| 74 | } |
|---|
| 75 | |
|---|
| 76 | # ------------------------------------------------------------------------- |
|---|
| 77 | # Generate a CRC lookup table. |
|---|
| 78 | # This creates a CRC algorithm lookup table for a 'width' bits checksum |
|---|
| 79 | # using the 'poly' polynomial for all values of an input byte. |
|---|
| 80 | # Setting 'reflected' changes the bit order for input bytes. |
|---|
| 81 | # Returns a list or 255 elements. |
|---|
| 82 | # |
|---|
| 83 | # CRC-32: Crc_table 32 $crc::polynomial(crc32) 1 |
|---|
| 84 | # CRC-16: Crc_table 16 $crc::polynomial(crc16) 1 |
|---|
| 85 | # CRC16/CCITT: Crc_table 16 $crc::polynomial(ccitt) 0 |
|---|
| 86 | # |
|---|
| 87 | proc ::crc::Crc_table {width poly reflected} { |
|---|
| 88 | set tbl {} |
|---|
| 89 | if {$width < 32} { |
|---|
| 90 | set mask [expr {(1 << $width) - 1}] |
|---|
| 91 | set topbit [expr {1 << ($width - 1)}] |
|---|
| 92 | } else { |
|---|
| 93 | set mask 0xffffffff |
|---|
| 94 | set topbit 0x80000000 |
|---|
| 95 | } |
|---|
| 96 | |
|---|
| 97 | for {set i 0} {$i < 256} {incr i} { |
|---|
| 98 | if {$reflected} { |
|---|
| 99 | set r [reflect $i 8] |
|---|
| 100 | } else { |
|---|
| 101 | set r $i |
|---|
| 102 | } |
|---|
| 103 | set r [expr {$r << ($width - 8)}] |
|---|
| 104 | for {set k 0} {$k < 8} {incr k} { |
|---|
| 105 | if {[expr {$r & $topbit}] != 0} { |
|---|
| 106 | set r [expr {($r << 1) ^ $poly}] |
|---|
| 107 | } else { |
|---|
| 108 | set r [expr {$r << 1}] |
|---|
| 109 | } |
|---|
| 110 | } |
|---|
| 111 | if {$reflected} { |
|---|
| 112 | set r [reflect $r $width] |
|---|
| 113 | } |
|---|
| 114 | lappend tbl [expr {$r & $mask}] |
|---|
| 115 | } |
|---|
| 116 | return $tbl |
|---|
| 117 | } |
|---|
| 118 | |
|---|
| 119 | # ------------------------------------------------------------------------- |
|---|
| 120 | # Calculate the CRC checksum for the data in 's' using a precalculated |
|---|
| 121 | # table. |
|---|
| 122 | # s the input data |
|---|
| 123 | # width - the width in bits of the CRC algorithm |
|---|
| 124 | # table - the name of the variable holding the calculated table |
|---|
| 125 | # init - the start value (or the last CRC for sequential blocks) |
|---|
| 126 | # xorout - the final value may be XORd with this value |
|---|
| 127 | # reflected - a boolean indicating that the bit order is reversed. |
|---|
| 128 | # For hardware optimised CRC checks, the bits are handled |
|---|
| 129 | # in transmission order (ie: bit0, bit1, ..., bit7) |
|---|
| 130 | proc ::crc::Crc {s width table {init 0} {xorout 0} {reflected 0}} { |
|---|
| 131 | upvar $table tbl |
|---|
| 132 | variable signbit |
|---|
| 133 | set signmask [expr {~$signbit>>7}] |
|---|
| 134 | |
|---|
| 135 | if {$width < 32} { |
|---|
| 136 | set mask [expr {(1 << $width) - 1}] |
|---|
| 137 | set rot [expr {$width - 8}] |
|---|
| 138 | } else { |
|---|
| 139 | set mask 0xffffffff |
|---|
| 140 | set rot 24 |
|---|
| 141 | } |
|---|
| 142 | |
|---|
| 143 | set crc $init |
|---|
| 144 | binary scan $s c* data |
|---|
| 145 | foreach {datum} $data { |
|---|
| 146 | if {$reflected} { |
|---|
| 147 | set ndx [expr {($crc ^ $datum) & 0xFF}] |
|---|
| 148 | set lkp [lindex $tbl $ndx] |
|---|
| 149 | set crc [expr {($lkp ^ ($crc >> 8 & $signmask)) & $mask}] |
|---|
| 150 | } else { |
|---|
| 151 | set ndx [expr {(($crc >> $rot) ^ $datum) & 0xFF}] |
|---|
| 152 | set lkp [lindex $tbl $ndx] |
|---|
| 153 | set crc [expr {($lkp ^ ($crc << 8 & $signmask)) & $mask}] |
|---|
| 154 | } |
|---|
| 155 | } |
|---|
| 156 | |
|---|
| 157 | return [expr {$crc ^ $xorout}] |
|---|
| 158 | } |
|---|
| 159 | |
|---|
| 160 | # ------------------------------------------------------------------------- |
|---|
| 161 | # Reverse the bit ordering for 'b' bits of the input value 'v' |
|---|
| 162 | proc ::crc::reflect {v b} { |
|---|
| 163 | set t $v |
|---|
| 164 | for {set i 0} {$i < $b} {incr i} { |
|---|
| 165 | set v [expr {($t & 1) ? ($v | (1<<(($b-1)-$i))) : ($v & ~(1<<(($b-1)-$i))) }] |
|---|
| 166 | set t [expr {$t >> 1}] |
|---|
| 167 | } |
|---|
| 168 | return $v |
|---|
| 169 | } |
|---|
| 170 | |
|---|
| 171 | # ------------------------------------------------------------------------- |
|---|
| 172 | # Description: |
|---|
| 173 | # Pop the nth element off a list. Used in options processing. |
|---|
| 174 | # |
|---|
| 175 | proc ::crc::Pop {varname {nth 0}} { |
|---|
| 176 | upvar $varname args |
|---|
| 177 | set r [lindex $args $nth] |
|---|
| 178 | set args [lreplace $args $nth $nth] |
|---|
| 179 | return $r |
|---|
| 180 | } |
|---|
| 181 | |
|---|
| 182 | # ------------------------------------------------------------------------- |
|---|
| 183 | # Specialisation of the general crc procedure to perform the standard CRC16 |
|---|
| 184 | # checksum |
|---|
| 185 | proc ::crc::CRC16 {s {seed 0}} { |
|---|
| 186 | variable table |
|---|
| 187 | if {![info exists table(crc16)]} { |
|---|
| 188 | variable polynomial |
|---|
| 189 | set table(crc16) [Crc_table 16 $polynomial(crc16) 1] |
|---|
| 190 | } |
|---|
| 191 | |
|---|
| 192 | return [Crc $s 16 [namespace current]::table(crc16) $seed 0 1] |
|---|
| 193 | } |
|---|
| 194 | |
|---|
| 195 | # ------------------------------------------------------------------------- |
|---|
| 196 | # Specialisation of the general crc procedure to perform the CCITT telecoms |
|---|
| 197 | # flavour of the CRC16 checksum |
|---|
| 198 | proc ::crc::CRC-CCITT {s {seed 0} {xor 0}} { |
|---|
| 199 | variable table |
|---|
| 200 | if {![info exists table(ccitt)]} { |
|---|
| 201 | variable polynomial |
|---|
| 202 | set table(ccitt) [Crc_table 16 $polynomial(ccitt) 0] |
|---|
| 203 | } |
|---|
| 204 | |
|---|
| 205 | return [Crc $s 16 [namespace current]::table(ccitt) $seed $xor 0] |
|---|
| 206 | } |
|---|
| 207 | |
|---|
| 208 | # ------------------------------------------------------------------------- |
|---|
| 209 | # Demostrates the parameters used for the 32 bit checksum CRC-32. |
|---|
| 210 | # This can be used to show the algorithm is working right by comparison with |
|---|
| 211 | # other crc32 implementations |
|---|
| 212 | proc ::crc::CRC-32 {s {seed 0xFFFFFFFF}} { |
|---|
| 213 | variable table |
|---|
| 214 | if {![info exists table(crc32)]} { |
|---|
| 215 | variable polynomial |
|---|
| 216 | set table(crc32) [Crc_table 32 $polynomial(crc32) 1] |
|---|
| 217 | } |
|---|
| 218 | |
|---|
| 219 | return [Crc $s 32 [namespace current]::table(crc32) $seed 0xFFFFFFFF 1] |
|---|
| 220 | } |
|---|
| 221 | |
|---|
| 222 | # ------------------------------------------------------------------------- |
|---|
| 223 | # User level CRC command. |
|---|
| 224 | proc ::crc::crc {args} { |
|---|
| 225 | array set opts [list filename {} channel {} chunksize 4096 \ |
|---|
| 226 | format %u seed 0 \ |
|---|
| 227 | impl [namespace origin CRC16]] |
|---|
| 228 | |
|---|
| 229 | while {[string match -* [set option [lindex $args 0]]]} { |
|---|
| 230 | switch -glob -- $option { |
|---|
| 231 | -fi* { set opts(filename) [Pop args 1] } |
|---|
| 232 | -cha* { set opts(channel) [Pop args 1] } |
|---|
| 233 | -chu* { set opts(chunksize) [Pop args 1] } |
|---|
| 234 | -fo* { set opts(format) [Pop args 1] } |
|---|
| 235 | -i* { set opts(impl) [uplevel 1 namespace origin [Pop args 1]] } |
|---|
| 236 | -s* { set opts(seed) [Pop args 1] } |
|---|
| 237 | -- { Pop args ; break } |
|---|
| 238 | default { |
|---|
| 239 | set options [join [lsort [array names opts]] ", -"] |
|---|
| 240 | return -code error "bad option $option:\ |
|---|
| 241 | must be one of -$options" |
|---|
| 242 | } |
|---|
| 243 | } |
|---|
| 244 | Pop args |
|---|
| 245 | } |
|---|
| 246 | |
|---|
| 247 | if {$opts(filename) != {}} { |
|---|
| 248 | set opts(channel) [open $opts(filename) r] |
|---|
| 249 | fconfigure $opts(channel) -translation binary |
|---|
| 250 | } |
|---|
| 251 | |
|---|
| 252 | if {$opts(channel) != {}} { |
|---|
| 253 | set r $opts(seed) |
|---|
| 254 | set trans [fconfigure $opts(channel) -translation] |
|---|
| 255 | fconfigure $opts(channel) -translation binary |
|---|
| 256 | while {![eof $opts(channel)]} { |
|---|
| 257 | set chunk [read $opts(channel) $opts(chunksize)] |
|---|
| 258 | set r [$opts(impl) $chunk $r] |
|---|
| 259 | } |
|---|
| 260 | fconfigure $opts(channel) -translation $trans |
|---|
| 261 | if {$opts(filename) != {}} { |
|---|
| 262 | close $opts(channel) |
|---|
| 263 | } |
|---|
| 264 | } else { |
|---|
| 265 | if {[llength $args] != 1} { |
|---|
| 266 | return -code error "wrong \# args: should be\ |
|---|
| 267 | \"crc16 ?-format string? ?-seed value? ?-impl procname?\ |
|---|
| 268 | -file name | data\"" |
|---|
| 269 | } |
|---|
| 270 | set r [$opts(impl) [lindex $args 0] $opts(seed)] |
|---|
| 271 | } |
|---|
| 272 | return [format $opts(format) $r] |
|---|
| 273 | } |
|---|
| 274 | |
|---|
| 275 | # ------------------------------------------------------------------------- |
|---|
| 276 | # The user commands. See 'crc' |
|---|
| 277 | # |
|---|
| 278 | proc ::crc::crc16 {args} { |
|---|
| 279 | return [eval [list crc -impl [namespace origin CRC16]] $args] |
|---|
| 280 | } |
|---|
| 281 | |
|---|
| 282 | proc ::crc::crc-ccitt {args} { |
|---|
| 283 | return [eval [list crc -impl [namespace origin CRC-CCITT] -seed 0xFFFF]\ |
|---|
| 284 | $args] |
|---|
| 285 | } |
|---|
| 286 | |
|---|
| 287 | proc ::crc::xmodem {args} { |
|---|
| 288 | return [eval [list crc -impl [namespace origin CRC-CCITT] -seed 0] $args] |
|---|
| 289 | } |
|---|
| 290 | |
|---|
| 291 | proc ::crc::crc-32 {args} { |
|---|
| 292 | return [eval [list crc -impl [namespace origin CRC-32] -seed 0xFFFFFFFF]\ |
|---|
| 293 | $args] |
|---|
| 294 | } |
|---|
| 295 | |
|---|
| 296 | # ------------------------------------------------------------------------- |
|---|
| 297 | |
|---|
| 298 | package provide crc16 $crc::crc16_version |
|---|
| 299 | |
|---|
| 300 | # ------------------------------------------------------------------------- |
|---|
| 301 | # |
|---|
| 302 | # Local variables: |
|---|
| 303 | # mode: tcl |
|---|
| 304 | # indent-tabs-mode: nil |
|---|
| 305 | # End: |
|---|