crc16.tcl

Go to the documentation of this file.
00001 /*  crc16.tcl -- Copyright (C) 2002 Pat Thoyts <patthoyts@users.sourceforge.net>*/
00002 /* */
00003 /*  Cyclic Redundancy Check - this is a Tcl implementation of a general*/
00004 /*  table-driven CRC implementation. This code should be able to generate*/
00005 /*  the lookup table and implement the correct algorithm for most types*/
00006 /*  of CRC. CRC-16, CRC-32 and the CCITT version of CRC-16. [1][2][3]*/
00007 /*  Most transmission CRCs use the CCITT polynomial (including X.25, SDLC*/
00008 /*  and Kermit).*/
00009 /* */
00010 /*  [1] http://www.microconsultants.com/tips/crc/crc.txt for the reference*/
00011 /*      implementation */
00012 /*  [2] http://www.embedded.com/internet/0001/0001connect.htm*/
00013 /*      for another good discussion of why things are the way they are.*/
00014 /*  [3] "Numerical Recipes in C", Press WH et al. Chapter 20.*/
00015 /* */
00016 /*  Checks: a crc for the string "123456789" should give:*/
00017 /*    CRC16:     0xBB3D*/
00018 /*    CRC-CCITT: 0x29B1*/
00019 /*    XMODEM:    0x31C3*/
00020 /*    CRC-32:    0xCBF43926*/
00021 /* */
00022 /*  eg: crc::crc16 "123456789"*/
00023 /*      crc::crc-ccitt "123456789"*/
00024 /*  or  crc::crc16 -file tclsh.exe*/
00025 /* */
00026 /*  Note:*/
00027 /*   The CCITT CRC can very easily be checked for the accuracy of transmission*/
00028 /*   as the CRC of the message plus the CRC values will be 0. That is:*/
00029 /*    % set msg {123456789]*/
00030 /*    % set crc [crc::crc-ccitt $msg]*/
00031 /*    % crc::crc-ccitt $msg[binary format S $crc]*/
00032 /*    0*/
00033 /* */
00034 /*   The same is true of other CRCs but some operate in reverse bit order:*/
00035 /*    % crc::crc16 $msg[binary format s [crc::crc16 $msg]]*/
00036 /*    0*/
00037 /* */
00038 /*  -------------------------------------------------------------------------*/
00039 /*  See the file "license.terms" for information on usage and redistribution*/
00040 /*  of this file, and for a DISCLAIMER OF ALL WARRANTIES.*/
00041 /*  -------------------------------------------------------------------------*/
00042 /*  $Id: crc16.tcl,v 1.15 2006/04/20 10:19:51 patthoyts Exp $*/
00043 
00044 /*  @mdgen EXCLUDE: crcc.tcl*/
00045 
00046 package require Tcl 8.2;                /*  tcl minimum version*/
00047 
00048 namespace ::crc {
00049     
00050     namespace export crc16 crc-ccitt crc-32
00051 
00052     variable crc16_version 1.1.1
00053 
00054     /*  Standard CRC generator polynomials.*/
00055     variable polynomial
00056      polynomial = (crc16) [expr {(1<<16) | (1<<15) | (1<<2) | 1}]
00057      polynomial = (ccitt) [expr {(1<<16) | (1<<12) | (1<<5) | 1}]
00058      polynomial = (crc32) [expr {(1<<32) | (1<<26) | (1<<23) | (1<<22) 
00059                                  | (1<<16) | (1<<12) | (1<<11) | (1<<10)
00060                                  | (1<<8) | (1<<7) | (1<<5) | (1<<4)
00061                                  | (1<<2) | (1<<1) | 1}]
00062 
00063     /*  Array to hold the generated tables*/
00064     variable table
00065     if {![info exists table]} { array  table =  {}}
00066 
00067     /*  calculate the sign bit for the current platform.*/
00068     variable signbit
00069     if {![info exists signbit]} {
00070     variable v
00071         for { v =  1} {int($v) != 0} { signbit =  $v;  v =  [expr {$v<<1}]} {}
00072         un v = 
00073     }
00074 }
00075 
00076 /*  -------------------------------------------------------------------------*/
00077 /*  Generate a CRC lookup table.*/
00078 /*  This creates a CRC algorithm lookup table for a 'width' bits checksum*/
00079 /*  using the 'poly' polynomial for all values of an input byte.*/
00080 /*  Setting 'reflected' changes the bit order for input bytes.*/
00081 /*  Returns a list or 255 elements.*/
00082 /* */
00083 /*  CRC-32:      Crc_table 32 $crc::polynomial(crc32) 1*/
00084 /*  CRC-16:      Crc_table 16 $crc::polynomial(crc16) 1*/
00085 /*  CRC16/CCITT: Crc_table 16 $crc::polynomial(ccitt) 0*/
00086 /* */
00087 ret  ::crc::Crc_table (type width , type poly , type reflected) {
00088     set tbl {}
00089     if {$width < 32} {
00090         set mask   [expr {(1 << $width) - 1}]
00091         set topbit [expr {1 << ($width - 1)}]
00092     } else {
00093         set mask   0xffffffff
00094         set topbit 0x80000000
00095     }
00096 
00097     for {set i 0} {$i < 256} {incr i} {
00098         if {$reflected} {
00099             set r [reflect $i 8]
00100         } else {
00101             set r $i
00102         }
00103         set r [expr {$r << ($width - 8)}]
00104         for {set k 0} {$k < 8} {incr k} {
00105             if {[expr {$r & $topbit}] != 0} {
00106                 set r [expr {($r << 1) ^ $poly}]
00107             } else {
00108                 set r [expr {$r << 1}]
00109             }
00110         }
00111         if {$reflected} {
00112             set r [reflect $r $width]
00113         }
00114         lappend tbl [expr {$r & $mask}]
00115     }
00116     return $tbl
00117 }
00118 
00119 /*  -------------------------------------------------------------------------*/
00120 /*  Calculate the CRC checksum for the data in 's' using a precalculated*/
00121 /*  table.*/
00122 /*   s the input data*/
00123 /*   width - the width in bits of the CRC algorithm*/
00124 /*   table - the name of the variable holding the calculated table*/
00125 /*   init  - the start value (or the last CRC for sequential blocks)*/
00126 /*   xorout - the final value may be XORd with this value*/
00127 /*   reflected - a boolean indicating that the bit order is reversed.*/
00128 /*               For hardware optimised CRC checks, the bits are handled*/
00129 /*               in transmission order (ie: bit0, bit1, ..., bit7)*/
00130 ret  ::crc::Crc (type s , type width , type table , optional init =0 , optional xorout =0 , optional reflected =0) {
00131     upvar $table tbl
00132     variable signbit
00133     set signmask [expr {~$signbit>>7}]
00134 
00135     if {$width < 32} {
00136         set mask   [expr {(1 << $width) - 1}]
00137         set rot    [expr {$width - 8}]
00138     } else {
00139         set mask   0xffffffff
00140         set rot    24
00141     }
00142 
00143     set crc $init
00144     binary scan $s c* data
00145     foreach {datum} $data {
00146         if {$reflected} {
00147             set ndx [expr {($crc ^ $datum) & 0xFF}]
00148             set lkp [lindex $tbl $ndx]
00149             set crc [expr {($lkp ^ ($crc >> 8 & $signmask)) & $mask}]
00150         } else {
00151             set ndx [expr {(($crc >> $rot) ^ $datum) & 0xFF}]
00152             set lkp [lindex $tbl $ndx]
00153             set crc [expr {($lkp ^ ($crc << 8 & $signmask)) & $mask}]
00154         }
00155     }
00156 
00157     return [expr {$crc ^ $xorout}]
00158 }
00159 
00160 /*  -------------------------------------------------------------------------*/
00161 /*  Reverse the bit ordering for 'b' bits of the input value 'v'*/
00162 ret  ::crc::reflect (type v , type b) {
00163     set t $v
00164     for {set i 0} {$i < $b} {incr i} {
00165         set v [expr {($t & 1) ? ($v | (1<<(($b-1)-$i))) : ($v & ~(1<<(($b-1)-$i))) }]
00166         set t [expr {$t >> 1}]
00167     }
00168     return $v
00169 }
00170 
00171 /*  -------------------------------------------------------------------------*/
00172 /*  Description:*/
00173 /*   Pop the nth element off a list. Used in options processing.*/
00174 /* */
00175 ret  ::crc::Pop (type varname , optional nth =0) {
00176     upvar $varname args
00177     set r [lindex $args $nth]
00178     set args [lreplace $args $nth $nth]
00179     return $r
00180 }
00181 
00182 /*  -------------------------------------------------------------------------*/
00183 /*  Specialisation of the general crc procedure to perform the standard CRC16*/
00184 /*  checksum*/
00185 ret  ::crc::CRC16 (type s , optional seed =0) {
00186     variable table
00187     if {![info exists table(crc16)]} {
00188         variable polynomial
00189         set table(crc16) [Crc_table 16 $polynomial(crc16) 1]
00190     }
00191 
00192     return [Crc $s 16 [namespace current]::table(crc16) $seed 0 1]
00193 }
00194 
00195 /*  -------------------------------------------------------------------------*/
00196 /*  Specialisation of the general crc procedure to perform the CCITT telecoms*/
00197 /*  flavour of the CRC16 checksum*/
00198 ret  ::crc::CRC-CCITT (type s , optional seed =0 , optional xor =0) {
00199     variable table
00200     if {![info exists table(ccitt)]} {
00201         variable polynomial
00202         set table(ccitt) [Crc_table 16 $polynomial(ccitt) 0]
00203     }
00204 
00205     return [Crc $s 16 [namespace current]::table(ccitt) $seed $xor 0]
00206 }
00207 
00208 /*  -------------------------------------------------------------------------*/
00209 /*  Demostrates the parameters used for the 32 bit checksum CRC-32.*/
00210 /*  This can be used to show the algorithm is working right by comparison with*/
00211 /*  other crc32 implementations*/
00212 ret  ::crc::CRC-32 (type s , optional seed =0xFFFFFFFF) {
00213     variable table
00214     if {![info exists table(crc32)]} {
00215         variable polynomial
00216         set table(crc32) [Crc_table 32 $polynomial(crc32) 1]
00217     }
00218 
00219     return [Crc $s 32 [namespace current]::table(crc32) $seed 0xFFFFFFFF 1]
00220 }
00221 
00222 /*  -------------------------------------------------------------------------*/
00223 /*  User level CRC command.*/
00224 ret  ::crc::crc (type args) {
00225     array set opts [list filename {} channel {} chunksize 4096 \
00226                         format %u  seed 0 \
00227                         impl [namespace origin CRC16]]
00228     
00229     while {[string match -* [set option [lindex $args 0]]]} {
00230         switch -glob -- $option {
00231             -fi*  { set opts(filename) [Pop args 1] }
00232             -cha* { set opts(channel) [Pop args 1] }
00233             -chu* { set opts(chunksize) [Pop args 1] }
00234             -fo*  { set opts(format) [Pop args 1] }
00235             -i*   { set opts(impl) [uplevel 1 namespace origin [Pop args 1]] }
00236             -s*   { set opts(seed) [Pop args 1] }
00237             --    { Pop args ; break }
00238             default {
00239                 set options [join [lsort [array names opts]] ", -"]
00240                 return -code error "bad option $option:\
00241                        must be one of -$options"
00242             }
00243         }
00244         Pop args
00245     }
00246 
00247     if {$opts(filename) != {}} {
00248         set opts(channel) [open $opts(filename) r]
00249         fconfigure $opts(channel) -translation binary
00250     }
00251 
00252     if {$opts(channel) != {}} {
00253         set r $opts(seed)
00254         set trans [fconfigure $opts(channel) -translation]
00255         fconfigure $opts(channel) -translation binary
00256         while {![eof $opts(channel)]} {
00257             set chunk [read $opts(channel) $opts(chunksize)]
00258             set r [$opts(impl) $chunk $r]
00259         }
00260         fconfigure $opts(channel) -translation $trans
00261         if {$opts(filename) != {}} {
00262             close $opts(channel)
00263         }
00264     } else {
00265         if {[llength $args] != 1} {
00266             return -code error "wrong \# args: should be\
00267                    \"crc16 ?-format string? ?-seed value? ?-impl procname?\
00268                    -file name | data\""
00269         }
00270         set r [$opts(impl) [lindex $args 0] $opts(seed)]
00271     }
00272     return [format $opts(format) $r]
00273 }
00274 
00275 /*  -------------------------------------------------------------------------*/
00276 /*  The user commands. See 'crc'*/
00277 /* */
00278 ret  ::crc::crc16 (type args) {
00279     return [eval [list crc -impl [namespace origin CRC16]] $args]
00280 }
00281 
00282 ret  ::crc::crc-ccitt (type args) {
00283     return [eval [list crc -impl [namespace origin CRC-CCITT] -seed 0xFFFF]\
00284                 $args]
00285 }
00286 
00287 ret  ::crc::xmodem (type args) {
00288     return [eval [list crc -impl [namespace origin CRC-CCITT] -seed 0] $args]
00289 }
00290 
00291 ret  ::crc::crc-32 (type args) {
00292     return [eval [list crc -impl [namespace origin CRC-32] -seed 0xFFFFFFFF]\
00293                 $args]
00294 }
00295 
00296 /*  -------------------------------------------------------------------------*/
00297 
00298 package provide crc16 $crc::crc16_version
00299 
00300 /*  -------------------------------------------------------------------------*/
00301 /* */
00302 /*  Local variables:*/
00303 /*    mode: tcl*/
00304 /*    indent-tabs-mode: nil*/
00305 /*  End:*/
00306 

Generated on 21 Sep 2010 for Gui by  doxygen 1.6.1