summaryrefslogtreecommitdiffstats
path: root/src/cksum.c
diff options
context:
space:
mode:
Diffstat (limited to 'src/cksum.c')
-rw-r--r--src/cksum.c298
1 files changed, 298 insertions, 0 deletions
diff --git a/src/cksum.c b/src/cksum.c
new file mode 100644
index 0000000..c74aef0
--- /dev/null
+++ b/src/cksum.c
@@ -0,0 +1,298 @@
+/* cksum -- calculate and print POSIX checksums and sizes of files
+ Copyright (C) 1992-2022 Free Software Foundation, Inc.
+
+ This program is free software: you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation, either version 3 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program. If not, see <https://www.gnu.org/licenses/>. */
+
+/* Written by Q. Frank Xia, qx@math.columbia.edu.
+ Cosmetic changes and reorganization by David MacKenzie, djm@gnu.ai.mit.edu.
+
+ Usage: cksum [file...]
+
+ The code segment between "#ifdef CRCTAB" and "#else" is the code
+ which calculates the "crctab". It is included for those who want
+ verify the correctness of the "crctab". To recreate the "crctab",
+ do something like the following:
+
+ cc -I../lib -DCRCTAB -o crctab cksum.c
+ crctab > crctab.c
+
+ This software is compatible with neither the System V nor the BSD
+ 'sum' program. It is supposed to conform to POSIX, except perhaps
+ for foreign language support. Any inconsistency with the standard
+ (other than foreign language support) is a bug. */
+
+#include <config.h>
+
+#include <stdio.h>
+#include <sys/types.h>
+#include <stdint.h>
+#include "system.h"
+
+#include <byteswap.h>
+#ifdef WORDS_BIGENDIAN
+# define SWAP(n) (n)
+#else
+# define SWAP(n) bswap_32 (n)
+#endif
+
+#ifdef CRCTAB
+
+# define BIT(x) ((uint_fast32_t) 1 << (x))
+# define SBIT BIT (31)
+
+/* The generating polynomial is
+
+ 32 26 23 22 16 12 11 10 8 7 5 4 2 1
+ G(X)=X + X + X + X + X + X + X + X + X + X + X + X + X + X + 1
+
+ The i bit in GEN is set if X^i is a summand of G(X) except X^32. */
+
+# define GEN (BIT (26) | BIT (23) | BIT (22) | BIT (16) | BIT (12) \
+ | BIT (11) | BIT (10) | BIT (8) | BIT (7) | BIT (5) \
+ | BIT (4) | BIT (2) | BIT (1) | BIT (0))
+
+static uint_fast32_t r[8];
+
+static void
+fill_r (void)
+{
+ r[0] = GEN;
+ for (int i = 1; i < 8; i++)
+ r[i] = (r[i - 1] << 1) ^ ((r[i - 1] & SBIT) ? GEN : 0);
+}
+
+static uint_fast32_t
+crc_remainder (int m)
+{
+ uint_fast32_t rem = 0;
+
+ for (int i = 0; i < 8; i++)
+ if (BIT (i) & m)
+ rem ^= r[i];
+
+ return rem & 0xFFFFFFFF; /* Make it run on 64-bit machine. */
+}
+
+int
+main (void)
+{
+ int i;
+ static uint_fast32_t crctab[8][256];
+
+ fill_r ();
+
+ for (i = 0; i < 256; i++)
+ {
+ crctab[0][i] = crc_remainder (i);
+ }
+
+ /* CRC(0x11 0x22 0x33 0x44)
+ is equal to
+ CRC(0x11 0x00 0x00 0x00) XOR CRC(0x22 0x00 0x00) XOR
+ CRC(0x33 0x00) XOR CRC(0x44)
+ We precompute the CRC values for the offset values into
+ separate CRC tables. We can then use them to speed up
+ CRC calculation by processing multiple bytes at the time. */
+ for (i = 0; i < 256; i++)
+ {
+ uint32_t crc = 0;
+
+ crc = (crc << 8) ^ crctab[0][((crc >> 24) ^ (i & 0xFF)) & 0xFF];
+ for (unsigned int offset = 1; offset < 8; offset++)
+ {
+ crc = (crc << 8) ^ crctab[0][((crc >> 24) ^ 0x00) & 0xFF];
+ crctab[offset][i] = crc;
+ }
+ }
+
+ printf ("#include <stdint.h>\n\n");
+ printf ("uint_fast32_t const crctab[8][256] = {\n");
+ for (int y = 0; y < 8; y++)
+ {
+ printf ("{\n 0x%08x", crctab[y][0]);
+ for (i = 0; i < 51; i++)
+ {
+ printf (",\n 0x%08x, 0x%08x, 0x%08x, 0x%08x, 0x%08x",
+ crctab[y][i * 5 + 1], crctab[y][i * 5 + 2],
+ crctab[y][i * 5 + 3], crctab[y][i * 5 + 4],
+ crctab[y][i * 5 + 5]);
+ }
+ printf ("\n},\n");
+ }
+ printf ("};\n");
+ return EXIT_SUCCESS;
+}
+
+#else /* !CRCTAB */
+
+# include "die.h"
+# include "error.h"
+
+# include "cksum.h"
+# if USE_PCLMUL_CRC32
+# include "cpuid.h"
+# else
+# define cksum_pclmul cksum_slice8
+# endif /* USE_PCLMUL_CRC32 */
+
+/* Number of bytes to read at once. */
+# define BUFLEN (1 << 16)
+
+
+static bool
+cksum_slice8 (FILE *fp, uint_fast32_t *crc_out, uintmax_t *length_out);
+static bool
+ (*cksum_fp)(FILE *, uint_fast32_t *, uintmax_t *);
+
+static bool
+pclmul_supported (void)
+{
+# if USE_PCLMUL_CRC32
+ unsigned int eax = 0;
+ unsigned int ebx = 0;
+ unsigned int ecx = 0;
+ unsigned int edx = 0;
+
+ if (! __get_cpuid (1, &eax, &ebx, &ecx, &edx))
+ {
+ if (cksum_debug)
+ error (0, 0, "%s", _("failed to get cpuid"));
+ return false;
+ }
+
+ if (! (ecx & bit_PCLMUL) || ! (ecx & bit_AVX))
+ {
+ if (cksum_debug)
+ error (0, 0, "%s", _("pclmul support not detected"));
+ return false;
+ }
+
+ if (cksum_debug)
+ error (0, 0, "%s", _("using pclmul hardware support"));
+
+ return true;
+# else
+ if (cksum_debug)
+ error (0, 0, "%s", _("using generic hardware support"));
+ return false;
+# endif /* USE_PCLMUL_CRC32 */
+}
+
+static bool
+cksum_slice8 (FILE *fp, uint_fast32_t *crc_out, uintmax_t *length_out)
+{
+ uint32_t buf[BUFLEN / sizeof (uint32_t)];
+ uint_fast32_t crc = 0;
+ uintmax_t length = 0;
+ size_t bytes_read;
+
+ if (!fp || !crc_out || !length_out)
+ return false;
+
+ while ((bytes_read = fread (buf, 1, BUFLEN, fp)) > 0)
+ {
+ uint32_t *datap;
+
+ if (length + bytes_read < length)
+ {
+ errno = EOVERFLOW;
+ return false;
+ }
+ length += bytes_read;
+
+ if (bytes_read == 0)
+ {
+ if (ferror (fp))
+ return false;
+ }
+
+ /* Process multiples of 8 bytes */
+ datap = (uint32_t *)buf;
+ while (bytes_read >= 8)
+ {
+ uint32_t first = *datap++, second = *datap++;
+ crc ^= SWAP (first);
+ second = SWAP (second);
+ crc = (crctab[7][(crc >> 24) & 0xFF]
+ ^ crctab[6][(crc >> 16) & 0xFF]
+ ^ crctab[5][(crc >> 8) & 0xFF]
+ ^ crctab[4][(crc) & 0xFF]
+ ^ crctab[3][(second >> 24) & 0xFF]
+ ^ crctab[2][(second >> 16) & 0xFF]
+ ^ crctab[1][(second >> 8) & 0xFF]
+ ^ crctab[0][(second) & 0xFF]);
+ bytes_read -= 8;
+ }
+
+ /* And finish up last 0-7 bytes in a byte by byte fashion */
+ unsigned char *cp = (unsigned char *)datap;
+ while (bytes_read--)
+ crc = (crc << 8) ^ crctab[0][((crc >> 24) ^ *cp++) & 0xFF];
+ if (feof (fp))
+ break;
+ }
+
+ *crc_out = crc;
+ *length_out = length;
+
+ return true;
+}
+
+/* Calculate the checksum and length in bytes of stream STREAM.
+ Return -1 on error, 0 on success. */
+
+int
+crc_sum_stream (FILE *stream, void *resstream, uintmax_t *length)
+{
+ uintmax_t total_bytes = 0;
+ uint_fast32_t crc = 0;
+
+ if (! cksum_fp)
+ {
+ if (pclmul_supported ())
+ cksum_fp = cksum_pclmul;
+ else
+ cksum_fp = cksum_slice8;
+ }
+
+ if (! cksum_fp (stream, &crc, &total_bytes))
+ return -1;
+
+ *length = total_bytes;
+
+ for (; total_bytes; total_bytes >>= 8)
+ crc = (crc << 8) ^ crctab[0][((crc >> 24) ^ total_bytes) & 0xFF];
+ crc = ~crc & 0xFFFFFFFF;
+
+ unsigned int crc_out = crc;
+ memcpy (resstream, &crc_out, sizeof crc_out);
+
+ return 0;
+}
+
+/* Print the checksum and size to stdout.
+ If ARGS is true, also print the FILE name. */
+
+void
+output_crc (char const *file, int binary_file, void const *digest,
+ bool tagged, unsigned char delim, bool args, uintmax_t length)
+{
+ char length_buf[INT_BUFSIZE_BOUND (uintmax_t)];
+ printf ("%u %s", *(unsigned int *)digest, umaxtostr (length, length_buf));
+ if (args)
+ printf (" %s", file);
+ putchar (delim);
+}
+
+#endif /* !CRCTAB */