diff --git a/.gitignore b/.gitignore index cc4b63a..30b3f05 100644 --- a/.gitignore +++ b/.gitignore @@ -10,3 +10,4 @@ *.bundle *.so *.dll +*.idea diff --git a/ext/zlib/zlib.c b/ext/zlib/zlib.c index aad9f8d..5dfc908 100644 --- a/ext/zlib/zlib.c +++ b/ext/zlib/zlib.c @@ -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*); @@ -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. @@ -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+. * */ @@ -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. */ @@ -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 + */ +#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 --------*/ @@ -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)); diff --git a/test/zlib/test_zlib.rb b/test/zlib/test_zlib.rb index 15e5bd8..4a91739 100644 --- a/test/zlib/test_zlib.rb +++ b/test/zlib/test_zlib.rb @@ -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