Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

CRC32C Checksum support #83

Open
wants to merge 4 commits into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -10,3 +10,4 @@
*.bundle
*.so
*.dll
*.idea
255 changes: 245 additions & 10 deletions ext/zlib/zlib.c
Original file line number Diff line number Diff line change
Expand Up @@ -74,9 +74,13 @@ static ID id_dictionaries, id_read, id_buffer;
static NORETURN(void raise_zlib_error(int, const char*));
static VALUE rb_zlib_version(VALUE);
static VALUE do_checksum(int, VALUE*, checksum_func);
static VALUE crc32c(uLong, const Bytef*, z_size_t);
static VALUE rb_zlib_adler32(int, VALUE*, VALUE);
static VALUE rb_zlib_crc32(int, VALUE*, VALUE);
static VALUE rb_zlib_crc_table(VALUE);
static VALUE rb_zlib_crc32_table(VALUE);
static VALUE rb_zlib_crc32c(int, VALUE*, VALUE);
static const unsigned long crc32ctbl[256];
static VALUE rb_zlib_crc32c_table(VALUE);
static voidpf zlib_mem_alloc(voidpf, uInt, uInt);
static void zlib_mem_free(voidpf, voidpf);
static void finalizer_warn(const char*);
Expand Down Expand Up @@ -495,10 +499,10 @@ rb_zlib_adler32_combine(VALUE klass, VALUE adler1, VALUE adler2, VALUE len2)
*
* call-seq: Zlib.crc32(string, crc)
*
* Calculates CRC checksum for +string+, and returns updated value of +crc+. If
* +string+ is omitted, it returns the CRC initial value. If +crc+ is omitted, it
* Calculates CRC32 checksum for +string+, and returns updated value of +crc+. If
* +string+ is omitted, it returns the CRC32 initial value. If +crc+ is omitted, it
* assumes that the initial value is given to +crc+. If +string+ is an IO instance,
* reads from the IO until the IO returns nil and returns CRC checksum of all read
* reads from the IO until the IO returns nil and returns CRC32 checksum of all read
* data.
*
* FIXME: expression.
Expand All @@ -515,8 +519,8 @@ rb_zlib_crc32(int argc, VALUE *argv, VALUE klass)
*
* call-seq: Zlib.crc32_combine(crc1, crc2, len2)
*
* Combine two CRC-32 check values in to one. +crc1+ is the first CRC-32
* value, +crc2+ is the second CRC-32 value. +len2+ is the length of the
* Combine two CRC32 check values in to one. +crc1+ is the first CRC32
* value, +crc2+ is the second CRC32 value. +len2+ is the length of the
* string used to generate +crc2+.
*
*/
Expand All @@ -531,12 +535,12 @@ rb_zlib_crc32_combine(VALUE klass, VALUE crc1, VALUE crc2, VALUE len2)
#endif

/*
* Document-method: Zlib.crc_table
* Document-method: Zlib.crc32_table
*
* Returns the table for calculating CRC checksum as an array.
* Returns the table for calculating CRC32 checksum as an array.
*/
static VALUE
rb_zlib_crc_table(VALUE obj)
rb_zlib_crc32_table(VALUE obj)
{
#if !defined(HAVE_TYPE_Z_CRC_T)
/* z_crc_t is defined since zlib-1.2.7. */
Expand All @@ -555,7 +559,234 @@ rb_zlib_crc_table(VALUE obj)
return dst;
}

static uLong
crc32c(uLong crc, const Bytef *buf, z_size_t size)
{
const uint8_t *p = buf;

crc = crc ^ 0xFFFFFFFF;
while (size--) {
crc = crc32ctbl[(crc ^ *p++) & 0xff] ^ (crc >> 8);
}
return crc ^ 0xFFFFFFFF;
}

/*
* Document-method: Zlib.crc32c
*
* call-seq: Zlib.crc32c(string, crc)
*
* Calculates CRC32C checksum for +string+, and returns updated value of +crc+. If
* +string+ is omitted, it returns the CRC32C initial value. If +crc+ is omitted, it
* assumes that the initial value is given to +crc+. If +string+ is an IO instance,
* reads from the IO until the IO returns nil and returns CRC32C checksum of all read
* data.
*
* FIXME: expression.
*/
static VALUE
rb_zlib_crc32c(int argc, VALUE *argv, VALUE klass)
{
return do_checksum(argc, argv, &crc32c);
}

/*
* The following CRC32C code was adapted from a fork of the CRC32 code in zlib,
* including the surrounding functions.
* https://github.com/luvit/zlib/blob/8de57bce969eb9dafc1f1f5c256ac608d0a73ec4/crc32.c#L355
mullermp marked this conversation as resolved.
Show resolved Hide resolved
*/
#define GF2_DIM 32 /* dimension of GF(2) vectors (length of CRC) */
void gf2_matrix_square OF((unsigned long *, unsigned long *));
unsigned long gf2_matrix_times OF((unsigned long *, unsigned long));
static uLong crc32c_combine_(unsigned long, unsigned long, z_off64_t);

void gf2_matrix_square(unsigned long *square, unsigned long *mat)
{
int n;

for (n = 0; n < GF2_DIM; n++)
square[n] = gf2_matrix_times(mat, mat[n]);
}

unsigned long gf2_matrix_times(unsigned long *mat, unsigned long vec)
{
unsigned long sum;

sum = 0;
while (vec) {
if (vec & 1)
sum ^= *mat;
vec >>= 1;
mat++;
}
return sum;
}

static uLong crc32c_combine_(unsigned long crc1, unsigned long crc2, z_off64_t len2)
{
int n;
unsigned long row;
unsigned long even[GF2_DIM]; /* even-power-of-two zeros operator */
unsigned long odd[GF2_DIM]; /* odd-power-of-two zeros operator */

/* degenerate case (also disallow negative lengths) */
if (len2 <= 0)
return crc1;

/* put operator for one zero bit in odd */
odd[0] = 0x82f63b78UL; /* CRC-32C polynomial */
row = 1;
for (n = 1; n < GF2_DIM; n++) {
odd[n] = row;
row <<= 1;
}

/* put operator for two zero bits in even */
gf2_matrix_square(even, odd);

/* put operator for four zero bits in odd */
gf2_matrix_square(odd, even);

/* apply len2 zeros to crc1 (first square will put the operator for one
zero byte, eight zero bits, in even) */
do {
/* apply zeros operator for this bit of len2 */
gf2_matrix_square(even, odd);
if (len2 & 1)
crc1 = gf2_matrix_times(even, crc1);
len2 >>= 1;

/* if no more bits set, then done */
if (len2 == 0)
break;

/* another iteration of the loop with odd and even swapped */
gf2_matrix_square(odd, even);
if (len2 & 1)
crc1 = gf2_matrix_times(odd, crc1);
len2 >>= 1;

/* if no more bits set, then done */
} while (len2 != 0);

/* return combined crc */
crc1 ^= crc2;
return crc1;
}
/* End of adapted CRC32C combine code */

#ifdef HAVE_CRC32_COMBINE
/*
* Document-method: Zlib.crc32c_combine
*
* call-seq: Zlib.crc32c_combine(crc1, crc2, len2)
*
* Combine two CRC32C check values in to one. +crc1+ is the first CRC32C
* value, +crc2+ is the second CRC32C value. +len2+ is the length of the
* string used to generate +crc2+.
*
*/
static VALUE
rb_zlib_crc32c_combine(VALUE klass, VALUE crc1, VALUE crc2, VALUE len2)
{
return ULONG2NUM(
crc32c_combine_(NUM2ULONG(crc1), NUM2ULONG(crc2), NUM2LONG(len2)));
}
#else
#define rb_zlib_crc32c_combine rb_f_notimplement
#endif

static const unsigned long crc32ctbl[256] = {
0x00000000L, 0xF26B8303L, 0xE13B70F7L, 0x1350F3F4L,
0xC79A971FL, 0x35F1141CL, 0x26A1E7E8L, 0xD4CA64EBL,
0x8AD958CFL, 0x78B2DBCCL, 0x6BE22838L, 0x9989AB3BL,
0x4D43CFD0L, 0xBF284CD3L, 0xAC78BF27L, 0x5E133C24L,
0x105EC76FL, 0xE235446CL, 0xF165B798L, 0x030E349BL,
0xD7C45070L, 0x25AFD373L, 0x36FF2087L, 0xC494A384L,
0x9A879FA0L, 0x68EC1CA3L, 0x7BBCEF57L, 0x89D76C54L,
0x5D1D08BFL, 0xAF768BBCL, 0xBC267848L, 0x4E4DFB4BL,
0x20BD8EDEL, 0xD2D60DDDL, 0xC186FE29L, 0x33ED7D2AL,
0xE72719C1L, 0x154C9AC2L, 0x061C6936L, 0xF477EA35L,
0xAA64D611L, 0x580F5512L, 0x4B5FA6E6L, 0xB93425E5L,
0x6DFE410EL, 0x9F95C20DL, 0x8CC531F9L, 0x7EAEB2FAL,
0x30E349B1L, 0xC288CAB2L, 0xD1D83946L, 0x23B3BA45L,
0xF779DEAEL, 0x05125DADL, 0x1642AE59L, 0xE4292D5AL,
0xBA3A117EL, 0x4851927DL, 0x5B016189L, 0xA96AE28AL,
0x7DA08661L, 0x8FCB0562L, 0x9C9BF696L, 0x6EF07595L,
0x417B1DBCL, 0xB3109EBFL, 0xA0406D4BL, 0x522BEE48L,
0x86E18AA3L, 0x748A09A0L, 0x67DAFA54L, 0x95B17957L,
0xCBA24573L, 0x39C9C670L, 0x2A993584L, 0xD8F2B687L,
0x0C38D26CL, 0xFE53516FL, 0xED03A29BL, 0x1F682198L,
0x5125DAD3L, 0xA34E59D0L, 0xB01EAA24L, 0x42752927L,
0x96BF4DCCL, 0x64D4CECFL, 0x77843D3BL, 0x85EFBE38L,
0xDBFC821CL, 0x2997011FL, 0x3AC7F2EBL, 0xC8AC71E8L,
0x1C661503L, 0xEE0D9600L, 0xFD5D65F4L, 0x0F36E6F7L,
0x61C69362L, 0x93AD1061L, 0x80FDE395L, 0x72966096L,
0xA65C047DL, 0x5437877EL, 0x4767748AL, 0xB50CF789L,
0xEB1FCBADL, 0x197448AEL, 0x0A24BB5AL, 0xF84F3859L,
0x2C855CB2L, 0xDEEEDFB1L, 0xCDBE2C45L, 0x3FD5AF46L,
0x7198540DL, 0x83F3D70EL, 0x90A324FAL, 0x62C8A7F9L,
0xB602C312L, 0x44694011L, 0x5739B3E5L, 0xA55230E6L,
0xFB410CC2L, 0x092A8FC1L, 0x1A7A7C35L, 0xE811FF36L,
0x3CDB9BDDL, 0xCEB018DEL, 0xDDE0EB2AL, 0x2F8B6829L,
0x82F63B78L, 0x709DB87BL, 0x63CD4B8FL, 0x91A6C88CL,
0x456CAC67L, 0xB7072F64L, 0xA457DC90L, 0x563C5F93L,
0x082F63B7L, 0xFA44E0B4L, 0xE9141340L, 0x1B7F9043L,
0xCFB5F4A8L, 0x3DDE77ABL, 0x2E8E845FL, 0xDCE5075CL,
0x92A8FC17L, 0x60C37F14L, 0x73938CE0L, 0x81F80FE3L,
0x55326B08L, 0xA759E80BL, 0xB4091BFFL, 0x466298FCL,
0x1871A4D8L, 0xEA1A27DBL, 0xF94AD42FL, 0x0B21572CL,
0xDFEB33C7L, 0x2D80B0C4L, 0x3ED04330L, 0xCCBBC033L,
0xA24BB5A6L, 0x502036A5L, 0x4370C551L, 0xB11B4652L,
0x65D122B9L, 0x97BAA1BAL, 0x84EA524EL, 0x7681D14DL,
0x2892ED69L, 0xDAF96E6AL, 0xC9A99D9EL, 0x3BC21E9DL,
0xEF087A76L, 0x1D63F975L, 0x0E330A81L, 0xFC588982L,
0xB21572C9L, 0x407EF1CAL, 0x532E023EL, 0xA145813DL,
0x758FE5D6L, 0x87E466D5L, 0x94B49521L, 0x66DF1622L,
0x38CC2A06L, 0xCAA7A905L, 0xD9F75AF1L, 0x2B9CD9F2L,
0xFF56BD19L, 0x0D3D3E1AL, 0x1E6DCDEEL, 0xEC064EEDL,
0xC38D26C4L, 0x31E6A5C7L, 0x22B65633L, 0xD0DDD530L,
0x0417B1DBL, 0xF67C32D8L, 0xE52CC12CL, 0x1747422FL,
0x49547E0BL, 0xBB3FFD08L, 0xA86F0EFCL, 0x5A048DFFL,
0x8ECEE914L, 0x7CA56A17L, 0x6FF599E3L, 0x9D9E1AE0L,
0xD3D3E1ABL, 0x21B862A8L, 0x32E8915CL, 0xC083125FL,
0x144976B4L, 0xE622F5B7L, 0xF5720643L, 0x07198540L,
0x590AB964L, 0xAB613A67L, 0xB831C993L, 0x4A5A4A90L,
0x9E902E7BL, 0x6CFBAD78L, 0x7FAB5E8CL, 0x8DC0DD8FL,
0xE330A81AL, 0x115B2B19L, 0x020BD8EDL, 0xF0605BEEL,
0x24AA3F05L, 0xD6C1BC06L, 0xC5914FF2L, 0x37FACCF1L,
0x69E9F0D5L, 0x9B8273D6L, 0x88D28022L, 0x7AB90321L,
0xAE7367CAL, 0x5C18E4C9L, 0x4F48173DL, 0xBD23943EL,
0xF36E6F75L, 0x0105EC76L, 0x12551F82L, 0xE03E9C81L,
0x34F4F86AL, 0xC69F7B69L, 0xD5CF889DL, 0x27A40B9EL,
0x79B737BAL, 0x8BDCB4B9L, 0x988C474DL, 0x6AE7C44EL,
0xBE2DA0A5L, 0x4C4623A6L, 0x5F16D052L, 0xAD7D5351L
};

/*
* Document-method: Zlib.crc32c_table
*
* Returns the table for calculating CRC32C checksum as an array.
*/
static VALUE
rb_zlib_crc32c_table(VALUE obj)
{
#if !defined(HAVE_TYPE_Z_CRC_T)
/* z_crc_t is defined since zlib-1.2.7. */
typedef unsigned long z_crc_t;
#endif
const z_crc_t *crctbl;
VALUE dst;
int i;

crctbl = crc32ctbl;
dst = rb_ary_new2(256);

for (i = 0; i < 256; i++) {
rb_ary_push(dst, rb_uint2inum(crctbl[i]));
}
return dst;
}

/*-------- zstream - internal APIs --------*/

Expand Down Expand Up @@ -4682,7 +4913,11 @@ Init_zlib(void)
rb_define_module_function(mZlib, "adler32_combine", rb_zlib_adler32_combine, 3);
rb_define_module_function(mZlib, "crc32", rb_zlib_crc32, -1);
rb_define_module_function(mZlib, "crc32_combine", rb_zlib_crc32_combine, 3);
rb_define_module_function(mZlib, "crc_table", rb_zlib_crc_table, 0);
rb_define_module_function(mZlib, "crc_table", rb_zlib_crc32_table, 0);
rb_define_module_function(mZlib, "crc32_table", rb_zlib_crc32_table, 0);
rb_define_module_function(mZlib, "crc32c", rb_zlib_crc32c, -1);
rb_define_module_function(mZlib, "crc32c_combine", rb_zlib_crc32c_combine, 3);
rb_define_module_function(mZlib, "crc32c_table", rb_zlib_crc32c_table, 0);

/* The Ruby/zlib version string. */
rb_define_const(mZlib, "VERSION", rb_str_new2(RUBY_ZLIB_VERSION));
Expand Down
48 changes: 48 additions & 0 deletions test/zlib/test_zlib.rb
Original file line number Diff line number Diff line change
Expand Up @@ -1442,6 +1442,54 @@ def test_crc_table
t.each {|x| assert_kind_of(Integer, x) }
end

def test_crc32_table
t = Zlib.crc32_table
assert_instance_of(Array, t)
t.each {|x| assert_kind_of(Integer, x) }
end

def test_crc32c
assert_equal(0x00000000, Zlib.crc32c)
assert_equal(0xcfc4ae1d, Zlib.crc32c("foo"))
assert_equal(0xcfc4ae1d, Zlib.crc32c("o", Zlib.crc32c("fo")))
assert_equal(0x88a0fa6c, Zlib.crc32c("abc\x01\x02\x03" * 10000))
assert_equal(0x75349366, Zlib.crc32c("p", -305419897))
Tempfile.create("test_zlib_gzip_file_to_io") {|t|
File.binwrite(t.path, "foo")
t.rewind
assert_equal(0xcfc4ae1d, Zlib.crc32c(t))

t.rewind
crc = Zlib.crc32c(t.read(2))
assert_equal(0xcfc4ae1d, Zlib.crc32c(t, crc))

File.binwrite(t.path, "abc\x01\x02\x03" * 10000)
t.rewind
assert_equal(0x88a0fa6c, Zlib.crc32c(t))
}
end

def test_crc32c_combine
one = Zlib.crc32c("fo")
two = Zlib.crc32c("o")
begin
assert_equal(0xcfc4ae1d, Zlib.crc32c_combine(one, two, 1))
rescue NotImplementedError
omit "crc32_combine is not implemented"
rescue Test::Unit::AssertionFailedError
if /aix/ =~ RUBY_PLATFORM
omit "zconf.h in zlib does not handle _LARGE_FILES in AIX. Skip until it is fixed"
end
raise $!
end
end

def test_crc32c_table
t = Zlib.crc32c_table
assert_instance_of(Array, t)
t.each {|x| assert_kind_of(Integer, x) }
end

def test_inflate
s = Zlib::Deflate.deflate("foo")
z = Zlib::Inflate.new
Expand Down
Loading