From 970cc32c2bbe395930d80e07cf573e6ea7fddc7d Mon Sep 17 00:00:00 2001 From: couturie Date: Sun, 10 Mar 2019 09:48:19 +0100 Subject: [PATCH] IDA_new --- IDA_new/Makefile | 28 + IDA_new/RAPH.READ.ME | 9 + IDA_new/RAPH.READ.ME~ | 5 + IDA_new/gf-complete/.gitignore | 78 + IDA_new/gf-complete/AUTHORS | 0 IDA_new/gf-complete/COPYING | 32 + IDA_new/gf-complete/ChangeLog | 0 IDA_new/gf-complete/License.txt | 32 + IDA_new/gf-complete/Makefile.am | 10 + IDA_new/gf-complete/NEWS | 0 IDA_new/gf-complete/README | 21 + IDA_new/gf-complete/README.txt | 21 + IDA_new/gf-complete/autogen.sh | 2 + IDA_new/gf-complete/configure.ac | 87 + IDA_new/gf-complete/examples/Makefile.am | 37 + IDA_new/gf-complete/examples/gf_example_1.c | 58 + IDA_new/gf-complete/examples/gf_example_2.c | 107 + IDA_new/gf-complete/examples/gf_example_3.c | 74 + IDA_new/gf-complete/examples/gf_example_4.c | 69 + IDA_new/gf-complete/examples/gf_example_5.c | 78 + IDA_new/gf-complete/examples/gf_example_6.c | 84 + IDA_new/gf-complete/examples/gf_example_7.c | 75 + IDA_new/gf-complete/include/gf_complete.h | 204 + IDA_new/gf-complete/include/gf_cpu.h | 20 + IDA_new/gf-complete/include/gf_general.h | 61 + IDA_new/gf-complete/include/gf_int.h | 216 + IDA_new/gf-complete/include/gf_method.h | 20 + IDA_new/gf-complete/include/gf_rand.h | 22 + IDA_new/gf-complete/include/gf_w16.h | 66 + IDA_new/gf-complete/include/gf_w32.h | 71 + IDA_new/gf-complete/include/gf_w4.h | 63 + IDA_new/gf-complete/include/gf_w64.h | 50 + IDA_new/gf-complete/include/gf_w8.h | 99 + .../gf-complete/m4/ax_check_compile_flag.m4 | 72 + IDA_new/gf-complete/m4/ax_ext.m4 | 75 + IDA_new/gf-complete/manual/gf-complete.html | 3484 +++++++++++++++++ IDA_new/gf-complete/manual/image1.png | Bin 0 -> 32752 bytes IDA_new/gf-complete/manual/image2.png | Bin 0 -> 28317 bytes IDA_new/gf-complete/manual/image3.png | Bin 0 -> 42222 bytes IDA_new/gf-complete/manual/image4.png | Bin 0 -> 26886 bytes IDA_new/gf-complete/manual/image5.png | Bin 0 -> 51457 bytes IDA_new/gf-complete/manual/image6.png | Bin 0 -> 47306 bytes IDA_new/gf-complete/manual/image7.png | Bin 0 -> 21312 bytes IDA_new/gf-complete/manual/style.css | 404 ++ IDA_new/gf-complete/src/Makefile.am | 32 + IDA_new/gf-complete/src/gf.c | 1090 ++++++ IDA_new/gf-complete/src/gf_cpu.c | 180 + IDA_new/gf-complete/src/gf_general.c | 539 +++ IDA_new/gf-complete/src/gf_method.c | 193 + IDA_new/gf-complete/src/gf_rand.c | 80 + IDA_new/gf-complete/src/gf_w128.c | 1776 +++++++++ IDA_new/gf-complete/src/gf_w16.c | 2449 ++++++++++++ IDA_new/gf-complete/src/gf_w32.c | 2810 +++++++++++++ IDA_new/gf-complete/src/gf_w4.c | 2047 ++++++++++ IDA_new/gf-complete/src/gf_w64.c | 2235 +++++++++++ IDA_new/gf-complete/src/gf_w8.c | 2398 ++++++++++++ IDA_new/gf-complete/src/gf_wgen.c | 1019 +++++ IDA_new/gf-complete/src/neon/gf_w16_neon.c | 276 ++ IDA_new/gf-complete/src/neon/gf_w32_neon.c | 269 ++ IDA_new/gf-complete/src/neon/gf_w4_neon.c | 247 ++ IDA_new/gf-complete/src/neon/gf_w64_neon.c | 333 ++ IDA_new/gf-complete/src/neon/gf_w8_neon.c | 302 ++ IDA_new/gf-complete/test/Makefile.am | 11 + IDA_new/gf-complete/test/gf_unit.c | 458 +++ IDA_new/gf-complete/tools/Makefile.am | 56 + IDA_new/gf-complete/tools/gf_add.c | 114 + IDA_new/gf-complete/tools/gf_div.c | 68 + IDA_new/gf-complete/tools/gf_inline_time.c | 170 + IDA_new/gf-complete/tools/gf_methods.c | 246 ++ IDA_new/gf-complete/tools/gf_mult.c | 68 + IDA_new/gf-complete/tools/gf_poly.c | 275 ++ IDA_new/gf-complete/tools/gf_time.c | 232 ++ IDA_new/gf-complete/tools/test_simd.sh | 367 ++ IDA_new/gf-complete/tools/test_simd_qemu.sh | 258 ++ IDA_new/gf-complete/tools/time_tool.sh | 98 + IDA_new/ida_gf65_paper1 | Bin 0 -> 504456 bytes IDA_new/ida_gf65_paper1.cpp | 619 +++ IDA_new/jerasure | 1 + 78 files changed, 27150 insertions(+) create mode 100644 IDA_new/Makefile create mode 100644 IDA_new/RAPH.READ.ME create mode 100644 IDA_new/RAPH.READ.ME~ create mode 100644 IDA_new/gf-complete/.gitignore create mode 100644 IDA_new/gf-complete/AUTHORS create mode 100644 IDA_new/gf-complete/COPYING create mode 100644 IDA_new/gf-complete/ChangeLog create mode 100644 IDA_new/gf-complete/License.txt create mode 100644 IDA_new/gf-complete/Makefile.am create mode 100644 IDA_new/gf-complete/NEWS create mode 100644 IDA_new/gf-complete/README create mode 100644 IDA_new/gf-complete/README.txt create mode 100755 IDA_new/gf-complete/autogen.sh create mode 100644 IDA_new/gf-complete/configure.ac create mode 100644 IDA_new/gf-complete/examples/Makefile.am create mode 100644 IDA_new/gf-complete/examples/gf_example_1.c create mode 100644 IDA_new/gf-complete/examples/gf_example_2.c create mode 100644 IDA_new/gf-complete/examples/gf_example_3.c create mode 100644 IDA_new/gf-complete/examples/gf_example_4.c create mode 100644 IDA_new/gf-complete/examples/gf_example_5.c create mode 100644 IDA_new/gf-complete/examples/gf_example_6.c create mode 100644 IDA_new/gf-complete/examples/gf_example_7.c create mode 100644 IDA_new/gf-complete/include/gf_complete.h create mode 100644 IDA_new/gf-complete/include/gf_cpu.h create mode 100644 IDA_new/gf-complete/include/gf_general.h create mode 100644 IDA_new/gf-complete/include/gf_int.h create mode 100644 IDA_new/gf-complete/include/gf_method.h create mode 100644 IDA_new/gf-complete/include/gf_rand.h create mode 100644 IDA_new/gf-complete/include/gf_w16.h create mode 100644 IDA_new/gf-complete/include/gf_w32.h create mode 100644 IDA_new/gf-complete/include/gf_w4.h create mode 100644 IDA_new/gf-complete/include/gf_w64.h create mode 100644 IDA_new/gf-complete/include/gf_w8.h create mode 100644 IDA_new/gf-complete/m4/ax_check_compile_flag.m4 create mode 100644 IDA_new/gf-complete/m4/ax_ext.m4 create mode 100644 IDA_new/gf-complete/manual/gf-complete.html create mode 100644 IDA_new/gf-complete/manual/image1.png create mode 100644 IDA_new/gf-complete/manual/image2.png create mode 100644 IDA_new/gf-complete/manual/image3.png create mode 100644 IDA_new/gf-complete/manual/image4.png create mode 100644 IDA_new/gf-complete/manual/image5.png create mode 100644 IDA_new/gf-complete/manual/image6.png create mode 100644 IDA_new/gf-complete/manual/image7.png create mode 100644 IDA_new/gf-complete/manual/style.css create mode 100644 IDA_new/gf-complete/src/Makefile.am create mode 100644 IDA_new/gf-complete/src/gf.c create mode 100644 IDA_new/gf-complete/src/gf_cpu.c create mode 100644 IDA_new/gf-complete/src/gf_general.c create mode 100644 IDA_new/gf-complete/src/gf_method.c create mode 100644 IDA_new/gf-complete/src/gf_rand.c create mode 100644 IDA_new/gf-complete/src/gf_w128.c create mode 100644 IDA_new/gf-complete/src/gf_w16.c create mode 100644 IDA_new/gf-complete/src/gf_w32.c create mode 100644 IDA_new/gf-complete/src/gf_w4.c create mode 100644 IDA_new/gf-complete/src/gf_w64.c create mode 100644 IDA_new/gf-complete/src/gf_w8.c create mode 100644 IDA_new/gf-complete/src/gf_wgen.c create mode 100644 IDA_new/gf-complete/src/neon/gf_w16_neon.c create mode 100644 IDA_new/gf-complete/src/neon/gf_w32_neon.c create mode 100644 IDA_new/gf-complete/src/neon/gf_w4_neon.c create mode 100644 IDA_new/gf-complete/src/neon/gf_w64_neon.c create mode 100644 IDA_new/gf-complete/src/neon/gf_w8_neon.c create mode 100644 IDA_new/gf-complete/test/Makefile.am create mode 100644 IDA_new/gf-complete/test/gf_unit.c create mode 100644 IDA_new/gf-complete/tools/Makefile.am create mode 100644 IDA_new/gf-complete/tools/gf_add.c create mode 100644 IDA_new/gf-complete/tools/gf_div.c create mode 100644 IDA_new/gf-complete/tools/gf_inline_time.c create mode 100644 IDA_new/gf-complete/tools/gf_methods.c create mode 100644 IDA_new/gf-complete/tools/gf_mult.c create mode 100644 IDA_new/gf-complete/tools/gf_poly.c create mode 100644 IDA_new/gf-complete/tools/gf_time.c create mode 100755 IDA_new/gf-complete/tools/test_simd.sh create mode 100755 IDA_new/gf-complete/tools/test_simd_qemu.sh create mode 100644 IDA_new/gf-complete/tools/time_tool.sh create mode 100755 IDA_new/ida_gf65_paper1 create mode 100644 IDA_new/ida_gf65_paper1.cpp create mode 160000 IDA_new/jerasure diff --git a/IDA_new/Makefile b/IDA_new/Makefile new file mode 100644 index 0000000..89f8e9e --- /dev/null +++ b/IDA_new/Makefile @@ -0,0 +1,28 @@ + + + +CPP_FLAGS= -fopenmp -O3 -lc -lm + + +C_INCLUDE=gf-complete/include/ +#LIBS= -larmadillo -lopenblas + +#test_mat2: test_mat2.cpp +# g++ -std=c++11 $(CPP_FLAGS) -o $@ $< $(LIBS) + +#ida: ida.cpp +# g++ -std=c++11 $(CPP_FLAGS) -o $@ $< $(LIBS) + + +ida_gf64: ida_gf64.cpp + g++ -o $@ $< -std=c++11 -O3 -lm -mmmx -msse -msse2 -msse3 -mssse3 -msse4.1 -msse4.2 -mavx -g -O3 -Wall -I/home/couturie/ajeter/jerasure/include /home/couturie/ajeter/jerasure/src/.libs/jerasure.o /home/couturie/ajeter/jerasure/src/.libs/galois.o -lgf_complete -fpermissive + +ida_gf65: ida_gf65.cpp + g++ -o $@ $< -std=c++11 -O3 -lm -mmmx -msse -msse2 -msse3 -mssse3 -msse4.1 -msse4.2 -mavx -g -O3 -Wall -Ijerasure/include jerasure/src/.libs/jerasure.o jerasure/src/.libs/galois.o -lgf_complete -fpermissive -lpthread -I $(C_INCLUDE) + + +ida_gf65_paper1: ida_gf65_paper1.cpp + g++ -o $@ $< -std=c++11 -O3 -lm -mmmx -msse -msse2 -msse3 -mssse3 -msse4.1 -msse4.2 -mavx -g -O3 -Wall -Ijerasure/include jerasure/src/.libs/jerasure.o jerasure/src/.libs/galois.o -lgf_complete -fpermissive -lpthread -I $(C_INCLUDE) + +clean: + rm test_mat2 ida ida_gf64 ida_gf65_paper1 diff --git a/IDA_new/RAPH.READ.ME b/IDA_new/RAPH.READ.ME new file mode 100644 index 0000000..705476f --- /dev/null +++ b/IDA_new/RAPH.READ.ME @@ -0,0 +1,9 @@ +cd gf-complete + +./configure +make + + +cd jerasure +./configure +make diff --git a/IDA_new/RAPH.READ.ME~ b/IDA_new/RAPH.READ.ME~ new file mode 100644 index 0000000..c690233 --- /dev/null +++ b/IDA_new/RAPH.READ.ME~ @@ -0,0 +1,5 @@ +cd gf-complete + +./configure +make + diff --git a/IDA_new/gf-complete/.gitignore b/IDA_new/gf-complete/.gitignore new file mode 100644 index 0000000..bfc1dfc --- /dev/null +++ b/IDA_new/gf-complete/.gitignore @@ -0,0 +1,78 @@ +Makefile +Makefile.in +/autom4te.cache +/aclocal.m4 +/compile +/configure +/depcomp +/install-sh +/missing +include/config.h +include/config.h.in +include/config.h.in~ +include/stamp-h1 + +# Object files +*.o +*.ko +*.obj +*.elf + +# Libraries +*.lib +*.la +*.a + +# Shared objects (inc. Windows DLLs) +*.dll +*.lo +*.so +*.so.* +*.dylib + +# Executables +*.exe +*.out +*.app +*.i*86 +*.x86_64 +*.hex + +# Other stuff +.deps/ +.libs/ +/config.log +/config.status +/libtool +INSTALL +config.guess +config.sub +ltmain.sh +m4/libtool.m4 +m4/ltversion.m4 +m4/ltoptions.m4 +m4/ltsugar.m4 +m4/lt~obsolete.m4 +test-driver +src/.dirstamp +test-driver + +examples/gf_example_1 +examples/gf_example_2 +examples/gf_example_3 +examples/gf_example_4 +examples/gf_example_5 +examples/gf_example_6 +examples/gf_example_7 +test/gf_unit +tools/gf_add +tools/gf_div +tools/gf_inline_time +tools/gf_methods +tools/gf_mult +tools/gf_poly +tools/gf_time +tools/gf_unit_w* +tools/test-suite.log +tools/.qemu/ +tools/test_simd*.results* diff --git a/IDA_new/gf-complete/AUTHORS b/IDA_new/gf-complete/AUTHORS new file mode 100644 index 0000000..e69de29 diff --git a/IDA_new/gf-complete/COPYING b/IDA_new/gf-complete/COPYING new file mode 100644 index 0000000..df8d9ed --- /dev/null +++ b/IDA_new/gf-complete/COPYING @@ -0,0 +1,32 @@ +Copyright (c) 2013, James S. Plank, Ethan L. Miller, Kevin M. Greenan, +Benjamin A. Arnold, John A. Burnum, Adam W. Disney, Allen C. McBride +All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions +are met: + + - Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + + - Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in + the documentation and/or other materials provided with the + distribution. + + - Neither the name of the University of Tennessee nor the names of its + contributors may be used to endorse or promote products derived + from this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, +INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, +BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS +OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED +AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT +LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY +WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE +POSSIBILITY OF SUCH DAMAGE. diff --git a/IDA_new/gf-complete/ChangeLog b/IDA_new/gf-complete/ChangeLog new file mode 100644 index 0000000..e69de29 diff --git a/IDA_new/gf-complete/License.txt b/IDA_new/gf-complete/License.txt new file mode 100644 index 0000000..df8d9ed --- /dev/null +++ b/IDA_new/gf-complete/License.txt @@ -0,0 +1,32 @@ +Copyright (c) 2013, James S. Plank, Ethan L. Miller, Kevin M. Greenan, +Benjamin A. Arnold, John A. Burnum, Adam W. Disney, Allen C. McBride +All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions +are met: + + - Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + + - Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in + the documentation and/or other materials provided with the + distribution. + + - Neither the name of the University of Tennessee nor the names of its + contributors may be used to endorse or promote products derived + from this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, +INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, +BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS +OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED +AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT +LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY +WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE +POSSIBILITY OF SUCH DAMAGE. diff --git a/IDA_new/gf-complete/Makefile.am b/IDA_new/gf-complete/Makefile.am new file mode 100644 index 0000000..cfb293a --- /dev/null +++ b/IDA_new/gf-complete/Makefile.am @@ -0,0 +1,10 @@ +# Top-level GF-Complete AM file +# Distributes headers + +SUBDIRS = src tools test examples +ACLOCAL_AMFLAGS = -I m4 + +include_HEADERS = include/gf_complete.h include/gf_method.h include/gf_rand.h include/gf_general.h + +# display the output of failed TESTS after a failed make check +export VERBOSE = true diff --git a/IDA_new/gf-complete/NEWS b/IDA_new/gf-complete/NEWS new file mode 100644 index 0000000..e69de29 diff --git a/IDA_new/gf-complete/README b/IDA_new/gf-complete/README new file mode 100644 index 0000000..7fd2f04 --- /dev/null +++ b/IDA_new/gf-complete/README @@ -0,0 +1,21 @@ +This is GF-Complete, Revision 1.03. January 1, 2015. + +Authors: James S. Plank (University of Tennessee) + Ethan L. Miller (UC Santa Cruz) + Kevin M. Greenan (Box) + Benjamin A. Arnold (University of Tennessee) + John A. Burnum (University of Tennessee) + Adam W. Disney (University of Tennessee, + Allen C. McBride (University of Tennessee) + +The user's manual is in the file Manual.pdf. + +The online home for GF-Complete is: + + - https://jerasure.org/jerasure/gf-complete + +To compile, do: + + ./configure + make + sudo make install diff --git a/IDA_new/gf-complete/README.txt b/IDA_new/gf-complete/README.txt new file mode 100644 index 0000000..cd2d66e --- /dev/null +++ b/IDA_new/gf-complete/README.txt @@ -0,0 +1,21 @@ +This is GF-Complete, Revision 1.03. January 1, 2015. + +Authors: James S. Plank (University of Tennessee) + Ethan L. Miller (UC Santa Cruz) + Kevin M. Greenan (Box) + Benjamin A. Arnold (University of Tennessee) + John A. Burnum (University of Tennessee) + Adam W. Disney (University of Tennessee, + Allen C. McBride (University of Tennessee) + +The user's manual is in the file Manual.pdf. + +The online home for GF-Complete is: + + - http://jerasure.org/jerasure/gf-complete + +To compile, do: + + ./configure + make + sudo make install diff --git a/IDA_new/gf-complete/autogen.sh b/IDA_new/gf-complete/autogen.sh new file mode 100755 index 0000000..b483139 --- /dev/null +++ b/IDA_new/gf-complete/autogen.sh @@ -0,0 +1,2 @@ +#!/bin/sh +autoreconf --force --install -I m4 diff --git a/IDA_new/gf-complete/configure.ac b/IDA_new/gf-complete/configure.ac new file mode 100644 index 0000000..d696f6e --- /dev/null +++ b/IDA_new/gf-complete/configure.ac @@ -0,0 +1,87 @@ +# gf-complete autoconf template + +# FIXME - add project url as the last argument +AC_INIT(gf-complete, 1.0) + +# Override default CFLAGS +: ${CFLAGS="-Wall -Wpointer-arith -O3 -g"} + +AC_PREREQ([2.61]) + +AM_INIT_AUTOMAKE([no-dependencies foreign parallel-tests]) +LT_INIT # libtool + +AC_CONFIG_HEADER(include/config.h) + +dnl Needed when reconfiguring with 'autoreconf -i -s' +AC_CONFIG_MACRO_DIR([m4]) + +# This prevents './configure; make' from trying to run autotools. +AM_MAINTAINER_MODE([disable]) + +dnl Compiling with per-target flags requires AM_PROG_CC_C_O. +AC_PROG_CC + +# Check for functions to provide aligned memory +# +AC_CHECK_FUNCS([posix_memalign], + [found_memalign=yes; break]) + +AS_IF([test "x$found_memalign" != "xyes"], [AC_MSG_WARN([No function for aligned memory allocation found])]) + +AC_ARG_ENABLE([debug-functions], + AS_HELP_STRING([--enable-debug-func], [Enable debugging of functions selected])) +AS_IF([test "x$enable_debug_func" = "xyes"], [CPPFLAGS="$CPPFLAGS -DDEBUG_FUNCTIONS"]) + +AC_ARG_ENABLE([debug-cpu], + AS_HELP_STRING([--enable-debug-cpu], [Enable debugging of SIMD detection])) +AS_IF([test "x$enable_debug_cpu" = "xyes"], [CPPFLAGS="$CPPFLAGS -DDEBUG_CPU_DETECTION"]) + +AX_EXT() + +AC_ARG_ENABLE([neon], + AS_HELP_STRING([--disable-neon], [Build without NEON optimizations])) + +AS_IF([test "x$enable_neon" != "xno"], + [noneon_CPPFLAGS=$CPPFLAGS + CPPFLAGS="$CPPFLAGS $SIMD_FLAGS" + AC_CHECK_HEADER([arm_neon.h], + [have_neon=yes], + [have_neon=no + CPPFLAGS=$noneon_CPPFLAGS])], + [have_neon=no + AS_IF([test "x$ax_cv_have_neon_ext" = "xyes"], + [SIMD_FLAGS=""]) + ]) + +AS_IF([test "x$have_neon" = "xno"], + [AS_IF([test "x$enable_neon" = "xyes"], + [AC_MSG_ERROR([neon requested but arm_neon.h not found])]) + ]) +AM_CONDITIONAL([HAVE_NEON], [test "x$have_neon" = "xyes"]) + +AC_ARG_ENABLE([sse], + AS_HELP_STRING([--disable-sse], [Build without SSE optimizations]), + [if test "x$enableval" = "xno" ; then + SIMD_FLAGS="" + echo "DISABLED SSE!!!" + fi] +) + +AC_ARG_ENABLE([valgrind], + [AS_HELP_STRING([--enable-valgrind], [run tests with valgrind])], + [], + [enable_valgrind=no]) +AM_CONDITIONAL(ENABLE_VALGRIND, test "x$enable_valgrind" != xno) + +AC_ARG_ENABLE([avx], AS_HELP_STRING([--enable-avx], [Build with AVX optimizations])) +AX_CHECK_COMPILE_FLAG(-mavx, [ax_cv_support_avx=yes], []) + +AS_IF([test "x$enable_avx" = "xyes"], + [AS_IF([test "x$ax_cv_support_avx" = "xno"], + [AC_MSG_ERROR([AVX requested but compiler does not support -mavx])], + [SIMD_FLAGS="$SIMD_FLAGS -mavx"]) + ]) + +AC_CONFIG_FILES([Makefile src/Makefile tools/Makefile test/Makefile examples/Makefile]) +AC_OUTPUT diff --git a/IDA_new/gf-complete/examples/Makefile.am b/IDA_new/gf-complete/examples/Makefile.am new file mode 100644 index 0000000..a420bda --- /dev/null +++ b/IDA_new/gf-complete/examples/Makefile.am @@ -0,0 +1,37 @@ +# GF-Complete 'examples' AM file + +AM_CPPFLAGS = -I$(top_builddir)/include -I$(top_srcdir)/include +AM_CFLAGS = -O3 $(SIMD_FLAGS) -fPIC + +bin_PROGRAMS = gf_example_1 gf_example_2 gf_example_3 gf_example_4 \ + gf_example_5 gf_example_6 gf_example_7 + +gf_example_1_SOURCES = gf_example_1.c +#gf_example_1_LDFLAGS = -lgf_complete +gf_example_1_LDADD = ../src/libgf_complete.la + +gf_example_2_SOURCES = gf_example_2.c +#gf_example_2_LDFLAGS = -lgf_complete +gf_example_2_LDADD = ../src/libgf_complete.la + +gf_example_3_SOURCES = gf_example_3.c +#gf_example_3_LDFLAGS = -lgf_complete +gf_example_3_LDADD = ../src/libgf_complete.la + +gf_example_4_SOURCES = gf_example_4.c +#gf_example_4_LDFLAGS = -lgf_complete +gf_example_4_LDADD = ../src/libgf_complete.la + +gf_example_5_SOURCES = gf_example_5.c +#gf_example_5_LDFLAGS = -lgf_complete +gf_example_5_LDADD = ../src/libgf_complete.la + +gf_example_6_SOURCES = gf_example_6.c +#gf_example_6_LDFLAGS = -lgf_complete +gf_example_6_LDADD = ../src/libgf_complete.la + +gf_example_7_SOURCES = gf_example_7.c +#gf_example_7_LDFLAGS = -lgf_complete +gf_example_7_LDADD = ../src/libgf_complete.la + + diff --git a/IDA_new/gf-complete/examples/gf_example_1.c b/IDA_new/gf-complete/examples/gf_example_1.c new file mode 100644 index 0000000..a7a4155 --- /dev/null +++ b/IDA_new/gf-complete/examples/gf_example_1.c @@ -0,0 +1,58 @@ +/* + * GF-Complete: A Comprehensive Open Source Library for Galois Field Arithmetic + * James S. Plank, Ethan L. Miller, Kevin M. Greenan, + * Benjamin A. Arnold, John A. Burnum, Adam W. Disney, Allen C. McBride. + * + * gf_example_1.c + * + * Demonstrates using the procedures for examples in GF(2^w) for w <= 32. + */ + +#include +#include +#include +#include +#include +#include + +#include "gf_complete.h" +#include "gf_rand.h" + +void usage(char *s) +{ + fprintf(stderr, "usage: gf_example_1 w - w must be between 1 and 32\n"); + exit(1); +} + +int main(int argc, char **argv) +{ + uint32_t a, b, c; + int w; + gf_t gf; + + if (argc != 2) usage(NULL); + w = atoi(argv[1]); + if (w <= 0 || w > 32) usage("Bad w"); + + /* Get two random numbers in a and b */ + + MOA_Seed(time(0)); + a = MOA_Random_W(w, 0); + b = MOA_Random_W(w, 0); + + /* Create the proper instance of the gf_t object using defaults: */ + + gf_init_easy(&gf, w); + + /* And multiply a and b using the galois field: */ + + c = gf.multiply.w32(&gf, a, b); + printf("%u * %u = %u\n", a, b, c); + + /* Divide the product by a and b */ + + printf("%u / %u = %u\n", c, a, gf.divide.w32(&gf, c, a)); + printf("%u / %u = %u\n", c, b, gf.divide.w32(&gf, c, b)); + + exit(0); +} diff --git a/IDA_new/gf-complete/examples/gf_example_2.c b/IDA_new/gf-complete/examples/gf_example_2.c new file mode 100644 index 0000000..576d9a5 --- /dev/null +++ b/IDA_new/gf-complete/examples/gf_example_2.c @@ -0,0 +1,107 @@ +/* + * GF-Complete: A Comprehensive Open Source Library for Galois Field Arithmetic + * James S. Plank, Ethan L. Miller, Kevin M. Greenan, + * Benjamin A. Arnold, John A. Burnum, Adam W. Disney, Allen C. McBride. + * + * gf_example_2.c + * + * Demonstrates using the procedures for examples in GF(2^w) for w <= 32. + */ + +#include +#include +#include +#include +#include +#include + +#include "gf_complete.h" +#include "gf_rand.h" + +void usage(char *s) +{ + fprintf(stderr, "usage: gf_example_2 w - w must be between 1 and 32\n"); + exit(1); +} + +int main(int argc, char **argv) +{ + uint32_t a, b, c; + uint8_t *r1, *r2; + uint16_t *r16 = NULL; + uint32_t *r32 = NULL; + int w, i; + gf_t gf; + + if (argc != 2) usage(NULL); + w = atoi(argv[1]); + if (w <= 0 || w > 32) usage("Bad w"); + + /* Get two random numbers in a and b */ + + MOA_Seed(time(0)); + a = MOA_Random_W(w, 0); + b = MOA_Random_W(w, 0); + + /* Create the proper instance of the gf_t object using defaults: */ + + gf_init_easy(&gf, w); + + /* And multiply a and b using the galois field: */ + + c = gf.multiply.w32(&gf, a, b); + printf("%u * %u = %u\n", a, b, c); + + /* Divide the product by a and b */ + + printf("%u / %u = %u\n", c, a, gf.divide.w32(&gf, c, a)); + printf("%u / %u = %u\n", c, b, gf.divide.w32(&gf, c, b)); + + /* If w is 4, 8, 16 or 32, do a very small region operation */ + + if (w == 4 || w == 8 || w == 16 || w == 32) { + r1 = (uint8_t *) malloc(16); + r2 = (uint8_t *) malloc(16); + + if (w == 4 || w == 8) { + r1[0] = b; + for (i = 1; i < 16; i++) r1[i] = MOA_Random_W(8, 1); + } else if (w == 16) { + r16 = (uint16_t *) r1; + r16[0] = b; + for (i = 1; i < 8; i++) r16[i] = MOA_Random_W(16, 1); + } else { + r32 = (uint32_t *) r1; + r32[0] = b; + for (i = 1; i < 4; i++) r32[i] = MOA_Random_W(32, 1); + } + + gf.multiply_region.w32(&gf, r1, r2, a, 16, 0); + + printf("\nmultiply_region by 0x%x (%u)\n\n", a, a); + printf("R1 (the source): "); + if (w == 4) { + for (i = 0; i < 16; i++) printf(" %x %x", r1[i] >> 4, r1[i] & 0xf); + } else if (w == 8) { + for (i = 0; i < 16; i++) printf(" %02x", r1[i]); + } else if (w == 16) { + for (i = 0; i < 8; i++) printf(" %04x", r16[i]); + } else if (w == 32) { + for (i = 0; i < 4; i++) printf(" %08x", r32[i]); + } + printf("\nR2 (the product): "); + if (w == 4) { + for (i = 0; i < 16; i++) printf(" %x %x", r2[i] >> 4, r2[i] & 0xf); + } else if (w == 8) { + for (i = 0; i < 16; i++) printf(" %02x", r2[i]); + } else if (w == 16) { + r16 = (uint16_t *) r2; + for (i = 0; i < 8; i++) printf(" %04x", r16[i]); + } else if (w == 32) { + r32 = (uint32_t *) r2; + for (i = 0; i < 4; i++) printf(" %08x", r32[i]); + } + printf("\n"); + } + exit(0); +} diff --git a/IDA_new/gf-complete/examples/gf_example_3.c b/IDA_new/gf-complete/examples/gf_example_3.c new file mode 100644 index 0000000..d6fef87 --- /dev/null +++ b/IDA_new/gf-complete/examples/gf_example_3.c @@ -0,0 +1,74 @@ +/* + * GF-Complete: A Comprehensive Open Source Library for Galois Field Arithmetic + * James S. Plank, Ethan L. Miller, Kevin M. Greenan, + * Benjamin A. Arnold, John A. Burnum, Adam W. Disney, Allen C. McBride. + * + * gf_example_3.c + * + * Identical to example_2 except it works in GF(2^64) + */ + +#include +#include +#include +#include +#include +#include + +#include "gf_complete.h" +#include "gf_rand.h" + +void usage(char *s) +{ + fprintf(stderr, "usage: gf_example_3\n"); + exit(1); +} + +int main(int argc, char **argv) +{ + uint64_t a, b, c; + uint64_t *r1, *r2; + int i; + gf_t gf; + + if (argc != 1) usage(NULL); + + /* Get two random numbers in a and b */ + + MOA_Seed(time(0)); + a = MOA_Random_64(); + b = MOA_Random_64(); + + /* Create the proper instance of the gf_t object using defaults: */ + + gf_init_easy(&gf, 64); + + /* And multiply a and b using the galois field: */ + + c = gf.multiply.w64(&gf, a, b); + printf("%llx * %llx = %llx\n", (long long unsigned int) a, (long long unsigned int) b, (long long unsigned int) c); + + /* Divide the product by a and b */ + + printf("%llx / %llx = %llx\n", (long long unsigned int) c, (long long unsigned int) a, (long long unsigned int) gf.divide.w64(&gf, c, a)); + printf("%llx / %llx = %llx\n", (long long unsigned int) c, (long long unsigned int) b, (long long unsigned int) gf.divide.w64(&gf, c, b)); + + r1 = (uint64_t *) malloc(32); + r2 = (uint64_t *) malloc(32); + + r1[0] = b; + + for (i = 1; i < 4; i++) r1[i] = MOA_Random_64(); + + gf.multiply_region.w64(&gf, r1, r2, a, 32, 0); + + printf("\nmultiply_region by %llx\n\n", (long long unsigned int) a); + printf("R1 (the source): "); + for (i = 0; i < 4; i++) printf(" %016llx", (long long unsigned int) r1[i]); + + printf("\nR2 (the product): "); + for (i = 0; i < 4; i++) printf(" %016llx", (long long unsigned int) r2[i]); + printf("\n"); + + exit(0); +} diff --git a/IDA_new/gf-complete/examples/gf_example_4.c b/IDA_new/gf-complete/examples/gf_example_4.c new file mode 100644 index 0000000..17529b5 --- /dev/null +++ b/IDA_new/gf-complete/examples/gf_example_4.c @@ -0,0 +1,69 @@ +/* + * GF-Complete: A Comprehensive Open Source Library for Galois Field Arithmetic + * James S. Plank, Ethan L. Miller, Kevin M. Greenan, + * Benjamin A. Arnold, John A. Burnum, Adam W. Disney, Allen C. McBride. + * + * gf_example_4.c + * + * Identical to example_3 except it works in GF(2^128) + */ + +#include +#include +#include +#include +#include +#include + +#include "gf_complete.h" +#include "gf_rand.h" + +#define LLUI (long long unsigned int) + +void usage(char *s) +{ + fprintf(stderr, "usage: gf_example_3\n"); + exit(1); +} + +int main(int argc, char **argv) +{ + uint64_t a[2], b[2], c[2]; + uint64_t *r1, *r2; + int i; + gf_t gf; + + if (argc != 1) usage(NULL); + + /* Get two random numbers in a and b */ + + MOA_Seed(time(0)); + MOA_Random_128(a); + MOA_Random_128(b); + + /* Create the proper instance of the gf_t object using defaults: */ + + gf_init_easy(&gf, 128); + + /* And multiply a and b using the galois field: */ + + gf.multiply.w128(&gf, a, b, c); + printf("%016llx%016llx * %016llx%016llx =\n%016llx%016llx\n", + LLUI a[0], LLUI a[1], LLUI b[0], LLUI b[1], LLUI c[0], LLUI c[1]); + + r1 = (uint64_t *) malloc(32); + r2 = (uint64_t *) malloc(32); + + for (i = 0; i < 4; i++) r1[i] = MOA_Random_64(); + + gf.multiply_region.w128(&gf, r1, r2, a, 32, 0); + + printf("\nmultiply_region by %016llx%016llx\n\n", LLUI a[0], LLUI a[1]); + printf("R1 (the source): "); + for (i = 0; i < 4; i += 2) printf(" %016llx%016llx", LLUI r1[i], LLUI r1[i+1]); + + printf("\nR2 (the product): "); + for (i = 0; i < 4; i += 2) printf(" %016llx%016llx", LLUI r2[i], LLUI r2[i+1]); + printf("\n"); + exit(0); +} diff --git a/IDA_new/gf-complete/examples/gf_example_5.c b/IDA_new/gf-complete/examples/gf_example_5.c new file mode 100644 index 0000000..da6e9ca --- /dev/null +++ b/IDA_new/gf-complete/examples/gf_example_5.c @@ -0,0 +1,78 @@ +/* + * GF-Complete: A Comprehensive Open Source Library for Galois Field Arithmetic + * James S. Plank, Ethan L. Miller, Kevin M. Greenan, + * Benjamin A. Arnold, John A. Burnum, Adam W. Disney, Allen C. McBride. + * + * gf_example_5.c + * + * Demonstrating altmap and extract_word + */ + +#include +#include +#include +#include +#include +#include + +#include "gf_complete.h" +#include "gf_rand.h" + +void usage(char *s) +{ + fprintf(stderr, "usage: gf_example_5\n"); + exit(1); +} + +int main(int argc, char **argv) +{ + uint16_t *a, *b; + int i, j; + gf_t gf; + + if (gf_init_hard(&gf, 16, GF_MULT_SPLIT_TABLE, GF_REGION_ALTMAP, GF_DIVIDE_DEFAULT, + 0, 16, 4, NULL, NULL) == 0) { + fprintf(stderr, "gf_init_hard failed\n"); + exit(1); + } + + a = (uint16_t *) malloc(200); + b = (uint16_t *) malloc(200); + + a += 6; + b += 6; + + MOA_Seed(0); + + for (i = 0; i < 30; i++) a[i] = MOA_Random_W(16, 1); + + gf.multiply_region.w32(&gf, a, b, 0x1234, 30*2, 0); + + printf("a: 0x%lx b: 0x%lx\n", (unsigned long) a, (unsigned long) b); + + for (i = 0; i < 30; i += 10) { + printf("\n"); + printf(" "); + for (j = 0; j < 10; j++) printf(" %4d", i+j); + printf("\n"); + + printf("a:"); + for (j = 0; j < 10; j++) printf(" %04x", a[i+j]); + printf("\n"); + + printf("b:"); + for (j = 0; j < 10; j++) printf(" %04x", b[i+j]); + printf("\n"); + printf("\n"); + } + + for (i = 0; i < 15; i ++) { + printf("Word %2d: 0x%04x * 0x1234 = 0x%04x ", i, + gf.extract_word.w32(&gf, a, 30*2, i), + gf.extract_word.w32(&gf, b, 30*2, i)); + printf("Word %2d: 0x%04x * 0x1234 = 0x%04x\n", i+15, + gf.extract_word.w32(&gf, a, 30*2, i+15), + gf.extract_word.w32(&gf, b, 30*2, i+15)); + } + return 0; +} diff --git a/IDA_new/gf-complete/examples/gf_example_6.c b/IDA_new/gf-complete/examples/gf_example_6.c new file mode 100644 index 0000000..800a35f --- /dev/null +++ b/IDA_new/gf-complete/examples/gf_example_6.c @@ -0,0 +1,84 @@ +/* + * GF-Complete: A Comprehensive Open Source Library for Galois Field Arithmetic + * James S. Plank, Ethan L. Miller, Kevin M. Greenan, + * Benjamin A. Arnold, John A. Burnum, Adam W. Disney, Allen C. McBride. + * + * gf_example_6.c + * + * Demonstrating altmap and extract_word + */ + +#include +#include +#include +#include +#include +#include + +#include "gf_complete.h" +#include "gf_rand.h" + +void usage(char *s) +{ + fprintf(stderr, "usage: gf_example_6\n"); + exit(1); +} + +int main(int argc, char **argv) +{ + uint32_t *a, *b; + int i, j; + gf_t gf, gf_16; + + if (gf_init_hard(&gf_16, 16, GF_MULT_LOG_TABLE, GF_REGION_DEFAULT, GF_DIVIDE_DEFAULT, + 0, 0, 0, NULL, NULL) == 0) { + fprintf(stderr, "gf_init_hard (6) failed\n"); + exit(1); + } + + if (gf_init_hard(&gf, 32, GF_MULT_COMPOSITE, GF_REGION_ALTMAP, GF_DIVIDE_DEFAULT, + 0, 2, 0, &gf_16, NULL) == 0) { + fprintf(stderr, "gf_init_hard (32) failed\n"); + exit(1); + } + + a = (uint32_t *) malloc(200); + b = (uint32_t *) malloc(200); + + a += 3; + b += 3; + + MOA_Seed(0); + + for (i = 0; i < 30; i++) a[i] = MOA_Random_W(32, 1); + + gf.multiply_region.w32(&gf, a, b, 0x12345678, 30*4, 0); + + printf("a: 0x%lx b: 0x%lx\n", (unsigned long) a, (unsigned long) b); + + for (i = 0; i < 30; i += 10) { + printf("\n"); + printf(" "); + for (j = 0; j < 10; j++) printf(" %8d", i+j); + printf("\n"); + + printf("a:"); + for (j = 0; j < 10; j++) printf(" %08x", a[i+j]); + printf("\n"); + + printf("b:"); + for (j = 0; j < 10; j++) printf(" %08x", b[i+j]); + printf("\n"); + printf("\n"); + } + + for (i = 0; i < 15; i ++) { + printf("Word %2d: 0x%08x * 0x12345678 = 0x%08x ", i, + gf.extract_word.w32(&gf, a, 30*4, i), + gf.extract_word.w32(&gf, b, 30*4, i)); + printf("Word %2d: 0x%08x * 0x12345678 = 0x%08x\n", i+15, + gf.extract_word.w32(&gf, a, 30*4, i+15), + gf.extract_word.w32(&gf, b, 30*4, i+15)); + } + return 0; +} diff --git a/IDA_new/gf-complete/examples/gf_example_7.c b/IDA_new/gf-complete/examples/gf_example_7.c new file mode 100644 index 0000000..ee07d53 --- /dev/null +++ b/IDA_new/gf-complete/examples/gf_example_7.c @@ -0,0 +1,75 @@ +/* + * GF-Complete: A Comprehensive Open Source Library for Galois Field Arithmetic + * James S. Plank, Ethan L. Miller, Kevin M. Greenan, + * Benjamin A. Arnold, John A. Burnum, Adam W. Disney, Allen C. McBride. + * + * gf_example_7.c + * + * Demonstrating extract_word and Cauchy + */ + +#include +#include +#include +#include +#include +#include + +#include "gf_complete.h" +#include "gf_rand.h" + +void usage(char *s) +{ + fprintf(stderr, "usage: gf_example_7\n"); + exit(1); +} + +int main(int argc, char **argv) +{ + uint8_t *a, *b; + int i, j; + gf_t gf; + + if (gf_init_hard(&gf, 3, GF_MULT_TABLE, GF_REGION_CAUCHY, GF_DIVIDE_DEFAULT, 0, 0, 0, NULL, NULL) == 0) { + fprintf(stderr, "gf_init_hard failed\n"); + exit(1); + } + + a = (uint8_t *) malloc(3); + b = (uint8_t *) malloc(3); + + MOA_Seed(0); + + for (i = 0; i < 3; i++) a[i] = MOA_Random_W(8, 1); + + gf.multiply_region.w32(&gf, a, b, 5, 3, 0); + + printf("a: 0x%lx b: 0x%lx\n", (unsigned long) a, (unsigned long) b); + + printf("\n"); + printf("a: 0x%02x 0x%02x 0x%02x\n", a[0], a[1], a[2]); + printf("b: 0x%02x 0x%02x 0x%02x\n", b[0], b[1], b[2]); + printf("\n"); + + printf("a bits:"); + for (i = 0; i < 3; i++) { + printf(" "); + for (j = 7; j >= 0; j--) printf("%c", (a[i] & (1 << j)) ? '1' : '0'); + } + printf("\n"); + + printf("b bits:"); + for (i = 0; i < 3; i++) { + printf(" "); + for (j = 7; j >= 0; j--) printf("%c", (b[i] & (1 << j)) ? '1' : '0'); + } + printf("\n"); + + printf("\n"); + for (i = 0; i < 8; i++) { + printf("Word %2d: %d * 5 = %d\n", i, + gf.extract_word.w32(&gf, a, 3, i), + gf.extract_word.w32(&gf, b, 3, i)); + } + return 0; +} diff --git a/IDA_new/gf-complete/include/gf_complete.h b/IDA_new/gf-complete/include/gf_complete.h new file mode 100644 index 0000000..c4783e8 --- /dev/null +++ b/IDA_new/gf-complete/include/gf_complete.h @@ -0,0 +1,204 @@ +/* + * GF-Complete: A Comprehensive Open Source Library for Galois Field Arithmetic + * James S. Plank, Ethan L. Miller, Kevin M. Greenan, + * Benjamin A. Arnold, John A. Burnum, Adam W. Disney, Allen C. McBride. + * + * gf_complete.h + * + * The main include file for gf_complete. + */ + +#ifndef _GF_COMPLETE_H_ +#define _GF_COMPLETE_H_ +#include + +#ifdef INTEL_SSE4 + #ifdef __SSE4_2__ + #include + #endif + #ifdef __SSE4_1__ + #include + #endif +#endif + +#ifdef INTEL_SSSE3 + #include +#endif + +#ifdef INTEL_SSE2 + #include +#endif + +#ifdef INTEL_SSE4_PCLMUL + #include +#endif + +#if defined(ARM_NEON) + #include +#endif + + +/* These are the different ways to perform multiplication. + Not all are implemented for all values of w. + See the paper for an explanation of how they work. */ + +typedef enum {GF_MULT_DEFAULT, + GF_MULT_SHIFT, + GF_MULT_CARRY_FREE, + GF_MULT_CARRY_FREE_GK, + GF_MULT_GROUP, + GF_MULT_BYTWO_p, + GF_MULT_BYTWO_b, + GF_MULT_TABLE, + GF_MULT_LOG_TABLE, + GF_MULT_LOG_ZERO, + GF_MULT_LOG_ZERO_EXT, + GF_MULT_SPLIT_TABLE, + GF_MULT_COMPOSITE } gf_mult_type_t; + +/* These are the different ways to optimize region + operations. They are bits because you can compose them. + Certain optimizations only apply to certain gf_mult_type_t's. + Again, please see documentation for how to use these */ + +#define GF_REGION_DEFAULT (0x0) +#define GF_REGION_DOUBLE_TABLE (0x1) +#define GF_REGION_QUAD_TABLE (0x2) +#define GF_REGION_LAZY (0x4) +#define GF_REGION_SIMD (0x8) +#define GF_REGION_SSE (0x8) +#define GF_REGION_NOSIMD (0x10) +#define GF_REGION_NOSSE (0x10) +#define GF_REGION_ALTMAP (0x20) +#define GF_REGION_CAUCHY (0x40) + +typedef uint32_t gf_region_type_t; + +/* These are different ways to implement division. + Once again, it's best to use "DEFAULT". However, + there are times when you may want to experiment + with the others. */ + +typedef enum { GF_DIVIDE_DEFAULT, + GF_DIVIDE_MATRIX, + GF_DIVIDE_EUCLID } gf_division_type_t; + +/* We support w=4,8,16,32,64 and 128 with their own data types and + operations for multiplication, division, etc. We also support + a "gen" type so that you can do general gf arithmetic for any + value of w from 1 to 32. You can perform a "region" operation + on these if you use "CAUCHY" as the mapping. + */ + +typedef uint32_t gf_val_32_t; +typedef uint64_t gf_val_64_t; +typedef uint64_t *gf_val_128_t; + +extern int _gf_errno; +extern void gf_error(); + +typedef struct gf *GFP; + +typedef union gf_func_a_b { + gf_val_32_t (*w32) (GFP gf, gf_val_32_t a, gf_val_32_t b); + gf_val_64_t (*w64) (GFP gf, gf_val_64_t a, gf_val_64_t b); + void (*w128)(GFP gf, gf_val_128_t a, gf_val_128_t b, gf_val_128_t c); +} gf_func_a_b; + +typedef union { + gf_val_32_t (*w32) (GFP gf, gf_val_32_t a); + gf_val_64_t (*w64) (GFP gf, gf_val_64_t a); + void (*w128)(GFP gf, gf_val_128_t a, gf_val_128_t b); +} gf_func_a; + +typedef union { + void (*w32) (GFP gf, void *src, void *dest, gf_val_32_t val, int bytes, int add); + void (*w64) (GFP gf, void *src, void *dest, gf_val_64_t val, int bytes, int add); + void (*w128)(GFP gf, void *src, void *dest, gf_val_128_t val, int bytes, int add); +} gf_region; + +typedef union { + gf_val_32_t (*w32) (GFP gf, void *start, int bytes, int index); + gf_val_64_t (*w64) (GFP gf, void *start, int bytes, int index); + void (*w128)(GFP gf, void *start, int bytes, int index, gf_val_128_t rv); +} gf_extract; + +typedef struct gf { + gf_func_a_b multiply; + gf_func_a_b divide; + gf_func_a inverse; + gf_region multiply_region; + gf_extract extract_word; + void *scratch; +} gf_t; + +/* Initializes the GF to defaults. Pass it a pointer to a gf_t. + Returns 0 on failure, 1 on success. */ + +extern int gf_init_easy(GFP gf, int w); + +/* Initializes the GF changing the defaults. + Returns 0 on failure, 1 on success. + Pass it a pointer to a gf_t. + For mult_type and divide_type, use one of gf_mult_type_t gf_divide_type_t . + For region_type, OR together the GF_REGION_xxx's defined above. + Use 0 as prim_poly for defaults. Otherwise, the leading 1 is optional. + Use NULL for scratch_memory to have init_hard allocate memory. Otherwise, + use gf_scratch_size() to determine how big scratch_memory has to be. + */ + +extern int gf_init_hard(GFP gf, + int w, + int mult_type, + int region_type, + int divide_type, + uint64_t prim_poly, + int arg1, + int arg2, + GFP base_gf, + void *scratch_memory); + +/* Determines the size for scratch_memory. + Returns 0 on failure and non-zero on success. */ + +extern int gf_scratch_size(int w, + int mult_type, + int region_type, + int divide_type, + int arg1, + int arg2); + +/* This reports the gf_scratch_size of a gf_t that has already been created */ + +extern int gf_size(GFP gf); + +/* Frees scratch memory if gf_init_easy/gf_init_hard called malloc. + If recursive = 1, then it calls itself recursively on base_gf. */ + +extern int gf_free(GFP gf, int recursive); + +/* This is support for inline single multiplications and divisions. + I know it's yucky, but if you've got to be fast, you've got to be fast. + We support inlining for w=4, w=8 and w=16. + + To use inline multiplication and division with w=4 or 8, you should use the + default gf_t, or one with a single table. Otherwise, gf_w4/8_get_mult_table() + will return NULL. Similarly, with w=16, the gf_t must be LOG */ + +uint8_t *gf_w4_get_mult_table(GFP gf); +uint8_t *gf_w4_get_div_table(GFP gf); + +#define GF_W4_INLINE_MULTDIV(table, a, b) (table[((a)<<4)|(b)]) + +uint8_t *gf_w8_get_mult_table(GFP gf); +uint8_t *gf_w8_get_div_table(GFP gf); + +#define GF_W8_INLINE_MULTDIV(table, a, b) (table[(((uint32_t) (a))<<8)|(b)]) + +uint16_t *gf_w16_get_log_table(GFP gf); +uint16_t *gf_w16_get_mult_alog_table(GFP gf); +uint16_t *gf_w16_get_div_alog_table(GFP gf); + +#define GF_W16_INLINE_MULT(log, alog, a, b) ((a) == 0 || (b) == 0) ? 0 : (alog[(uint32_t)log[a]+(uint32_t)log[b]]) +#define GF_W16_INLINE_DIV(log, alog, a, b) ((a) == 0 || (b) == 0) ? 0 : (alog[(int)log[a]-(int)log[b]]) +#endif diff --git a/IDA_new/gf-complete/include/gf_cpu.h b/IDA_new/gf-complete/include/gf_cpu.h new file mode 100644 index 0000000..71c7227 --- /dev/null +++ b/IDA_new/gf-complete/include/gf_cpu.h @@ -0,0 +1,20 @@ +/* + * GF-Complete: A Comprehensive Open Source Library for Galois Field Arithmetic + * James S. Plank, Ethan L. Miller, Kevin M. Greenan, + * Benjamin A. Arnold, John A. Burnum, Adam W. Disney, Allen C. McBride. + * + * gf_cpu.h + * + * Identifies whether the CPU supports SIMD instructions at runtime. + */ + +#pragma once + +extern int gf_cpu_supports_intel_pclmul; +extern int gf_cpu_supports_intel_sse4; +extern int gf_cpu_supports_intel_ssse3; +extern int gf_cpu_supports_intel_sse3; +extern int gf_cpu_supports_intel_sse2; +extern int gf_cpu_supports_arm_neon; + +void gf_cpu_identify(void); diff --git a/IDA_new/gf-complete/include/gf_general.h b/IDA_new/gf-complete/include/gf_general.h new file mode 100644 index 0000000..9a5de52 --- /dev/null +++ b/IDA_new/gf-complete/include/gf_general.h @@ -0,0 +1,61 @@ +/* + * GF-Complete: A Comprehensive Open Source Library for Galois Field Arithmetic + * James S. Plank, Ethan L. Miller, Kevin M. Greenan, + * Benjamin A. Arnold, John A. Burnum, Adam W. Disney, Allen C. McBride. + * + * gf_general.h + * + * This file has helper routines for doing basic GF operations with any + * legal value of w. The problem is that w <= 32, w=64 and w=128 all have + * different data types, which is a pain. The procedures in this file try + * to alleviate that pain. They are used in gf_unit and gf_time. + */ + +#pragma once + +#include +#include +#include +#include +#include +#include + +#include "gf_complete.h" + +typedef union { + uint32_t w32; + uint64_t w64; + uint64_t w128[2]; +} gf_general_t; + +void gf_general_set_zero(gf_general_t *v, int w); +void gf_general_set_one(gf_general_t *v, int w); +void gf_general_set_two(gf_general_t *v, int w); + +int gf_general_is_zero(gf_general_t *v, int w); +int gf_general_is_one(gf_general_t *v, int w); +int gf_general_are_equal(gf_general_t *v1, gf_general_t *v2, int w); + +void gf_general_val_to_s(gf_general_t *v, int w, char *s, int hex); +int gf_general_s_to_val(gf_general_t *v, int w, char *s, int hex); + +void gf_general_set_random(gf_general_t *v, int w, int zero_ok); + +void gf_general_add(gf_t *gf, gf_general_t *a, gf_general_t *b, gf_general_t *c); +void gf_general_multiply(gf_t *gf, gf_general_t *a, gf_general_t *b, gf_general_t *c); +void gf_general_divide(gf_t *gf, gf_general_t *a, gf_general_t *b, gf_general_t *c); +void gf_general_inverse(gf_t *gf, gf_general_t *a, gf_general_t *b); + +void gf_general_do_region_multiply(gf_t *gf, gf_general_t *a, + void *ra, void *rb, + int bytes, int xor); + +void gf_general_do_region_check(gf_t *gf, gf_general_t *a, + void *orig_a, void *orig_target, void *final_target, + int bytes, int xor); + + +/* Which is M, D or I for multiply, divide or inverse. */ + +void gf_general_set_up_single_timing_test(int w, void *ra, void *rb, int size); +int gf_general_do_single_timing_test(gf_t *gf, void *ra, void *rb, int size, char which); diff --git a/IDA_new/gf-complete/include/gf_int.h b/IDA_new/gf-complete/include/gf_int.h new file mode 100644 index 0000000..0356920 --- /dev/null +++ b/IDA_new/gf-complete/include/gf_int.h @@ -0,0 +1,216 @@ +/* + * GF-Complete: A Comprehensive Open Source Library for Galois Field Arithmetic + * James S. Plank, Ethan L. Miller, Kevin M. Greenan, + * Benjamin A. Arnold, John A. Burnum, Adam W. Disney, Allen C. McBride. + * + * gf_int.h + * + * Internal code for Galois field routines. This is not meant for + * users to include, but for the internal GF files to use. + */ + +#pragma once + +#include "gf_complete.h" + +#include + +extern void timer_start (double *t); +extern double timer_split (const double *t); +extern void galois_fill_random (void *buf, int len, unsigned int seed); + +typedef struct { + int mult_type; + int region_type; + int divide_type; + int w; + uint64_t prim_poly; + int free_me; + int arg1; + int arg2; + gf_t *base_gf; + void *private; +#ifdef DEBUG_FUNCTIONS + const char *multiply; + const char *divide; + const char *inverse; + const char *multiply_region; + const char *extract_word; +#endif +} gf_internal_t; + +#ifdef DEBUG_FUNCTIONS +#define SET_FUNCTION(gf,method,size,func) \ + { (gf)->method.size = (func); \ + ((gf_internal_t*)(gf)->scratch)->method = #func; } +#else +#define SET_FUNCTION(gf,method,size,func) \ + (gf)->method.size = (func); +#endif + +extern int gf_w4_init (gf_t *gf); +extern int gf_w4_scratch_size(int mult_type, int region_type, int divide_type, int arg1, int arg2); + +extern int gf_w8_init (gf_t *gf); +extern int gf_w8_scratch_size(int mult_type, int region_type, int divide_type, int arg1, int arg2); + +extern int gf_w16_init (gf_t *gf); +extern int gf_w16_scratch_size(int mult_type, int region_type, int divide_type, int arg1, int arg2); + +extern int gf_w32_init (gf_t *gf); +extern int gf_w32_scratch_size(int mult_type, int region_type, int divide_type, int arg1, int arg2); + +extern int gf_w64_init (gf_t *gf); +extern int gf_w64_scratch_size(int mult_type, int region_type, int divide_type, int arg1, int arg2); + +extern int gf_w128_init (gf_t *gf); +extern int gf_w128_scratch_size(int mult_type, int region_type, int divide_type, int arg1, int arg2); + +extern int gf_wgen_init (gf_t *gf); +extern int gf_wgen_scratch_size(int w, int mult_type, int region_type, int divide_type, int arg1, int arg2); + +void gf_wgen_cauchy_region(gf_t *gf, void *src, void *dest, gf_val_32_t val, int bytes, int xor); +gf_val_32_t gf_wgen_extract_word(gf_t *gf, void *start, int bytes, int index); + +extern void gf_alignment_error(char *s, int a); + +extern uint32_t gf_bitmatrix_inverse(uint32_t y, int w, uint32_t pp); + +/* This returns the correct default for prim_poly when base is used as the base + field for COMPOSITE. It returns 0 if we don't have a default prim_poly. */ + +extern uint64_t gf_composite_get_default_poly(gf_t *base); + +/* This structure lets you define a region multiply. It helps because you can handle + unaligned portions of the data with the procedures below, which really cleans + up the code. */ + +typedef struct { + gf_t *gf; + void *src; + void *dest; + int bytes; + uint64_t val; + int xor; + int align; /* The number of bytes to which to align. */ + void *s_start; /* The start and the top of the aligned region. */ + void *d_start; + void *s_top; + void *d_top; +} gf_region_data; + +/* This lets you set up one of these in one call. It also sets the start/top pointers. */ + +void gf_set_region_data(gf_region_data *rd, + gf_t *gf, + void *src, + void *dest, + int bytes, + uint64_t val, + int xor, + int align); + +/* This performs gf->multiply.32() on all of the unaligned bytes in the beginning of the region */ + +extern void gf_do_initial_region_alignment(gf_region_data *rd); + +/* This performs gf->multiply.32() on all of the unaligned bytes in the end of the region */ + +extern void gf_do_final_region_alignment(gf_region_data *rd); + +extern void gf_two_byte_region_table_multiply(gf_region_data *rd, uint16_t *base); + +extern void gf_multby_zero(void *dest, int bytes, int xor); +extern void gf_multby_one(void *src, void *dest, int bytes, int xor); + +typedef enum {GF_E_MDEFDIV, /* Dev != Default && Mult == Default */ + GF_E_MDEFREG, /* Reg != Default && Mult == Default */ + GF_E_MDEFARG, /* Args != Default && Mult == Default */ + GF_E_DIVCOMP, /* Mult == Composite && Div != Default */ + GF_E_CAUCOMP, /* Mult == Composite && Reg == CAUCHY */ + GF_E_DOUQUAD, /* Reg == DOUBLE && Reg == QUAD */ + GF_E_SIMD_NO, /* Reg == SIMD && Reg == NOSIMD */ + GF_E_CAUCHYB, /* Reg == CAUCHY && Other Reg */ + GF_E_CAUGT32, /* Reg == CAUCHY && w > 32*/ + GF_E_ARG1SET, /* Arg1 != 0 && Mult \notin COMPOSITE/SPLIT/GROUP */ + GF_E_ARG2SET, /* Arg2 != 0 && Mult \notin SPLIT/GROUP */ + GF_E_MATRIXW, /* Div == MATRIX && w > 32 */ + GF_E_BAD___W, /* Illegal w */ + GF_E_DOUBLET, /* Reg == DOUBLE && Mult != TABLE */ + GF_E_DOUBLEW, /* Reg == DOUBLE && w \notin {4,8} */ + GF_E_DOUBLEJ, /* Reg == DOUBLE && other Reg */ + GF_E_DOUBLEL, /* Reg == DOUBLE & LAZY but w = 4 */ + GF_E_QUAD__T, /* Reg == QUAD && Mult != TABLE */ + GF_E_QUAD__W, /* Reg == QUAD && w != 4 */ + GF_E_QUAD__J, /* Reg == QUAD && other Reg */ + GF_E_LAZY__X, /* Reg == LAZY && not DOUBLE or QUAD*/ + GF_E_ALTSHIF, /* Mult == Shift && Reg == ALTMAP */ + GF_E_SSESHIF, /* Mult == Shift && Reg == SIMD|NOSIMD */ + GF_E_ALT_CFM, /* Mult == CARRY_FREE && Reg == ALTMAP */ + GF_E_SSE_CFM, /* Mult == CARRY_FREE && Reg == SIMD|NOSIMD */ + GF_E_PCLMULX, /* Mult == Carry_Free && No PCLMUL */ + GF_E_ALT_BY2, /* Mult == Bytwo_x && Reg == ALTMAP */ + GF_E_BY2_SSE, /* Mult == Bytwo_x && Reg == SSE && No SSE2 */ + GF_E_LOGBADW, /* Mult == LOGx, w too big*/ + GF_E_LOG___J, /* Mult == LOGx, && Reg == SSE|ALTMAP|NOSSE */ + GF_E_ZERBADW, /* Mult == LOG_ZERO, w \notin {8,16} */ + GF_E_ZEXBADW, /* Mult == LOG_ZERO_EXT, w != 8 */ + GF_E_LOGPOLY, /* Mult == LOG & poly not primitive */ + GF_E_GR_ARGX, /* Mult == GROUP, Bad arg1/2 */ + GF_E_GR_W_48, /* Mult == GROUP, w \in { 4, 8 } */ + GF_E_GR_W_16, /* Mult == GROUP, w == 16, arg1 != 4 || arg2 != 4 */ + GF_E_GR_128A, /* Mult == GROUP, w == 128, bad args */ + GF_E_GR_A_27, /* Mult == GROUP, either arg > 27 */ + GF_E_GR_AR_W, /* Mult == GROUP, either arg > w */ + GF_E_GR____J, /* Mult == GROUP, Reg == SSE|ALTMAP|NOSSE */ + GF_E_TABLE_W, /* Mult == TABLE, w too big */ + GF_E_TAB_SSE, /* Mult == TABLE, SIMD|NOSIMD only apply to w == 4 */ + GF_E_TABSSE3, /* Mult == TABLE, Need SSSE3 for SSE */ + GF_E_TAB_ALT, /* Mult == TABLE, Reg == ALTMAP */ + GF_E_SP128AR, /* Mult == SPLIT, w=128, Bad arg1/arg2 */ + GF_E_SP128AL, /* Mult == SPLIT, w=128, SSE requires ALTMAP */ + GF_E_SP128AS, /* Mult == SPLIT, w=128, ALTMAP requires SSE */ + GF_E_SP128_A, /* Mult == SPLIT, w=128, ALTMAP only with 4/128 */ + GF_E_SP128_S, /* Mult == SPLIT, w=128, SSE only with 4/128 */ + GF_E_SPLIT_W, /* Mult == SPLIT, Bad w (8, 16, 32, 64, 128) */ + GF_E_SP_16AR, /* Mult == SPLIT, w=16, Bad arg1/arg2 */ + GF_E_SP_16_A, /* Mult == SPLIT, w=16, ALTMAP only with 4/16 */ + GF_E_SP_16_S, /* Mult == SPLIT, w=16, SSE only with 4/16 */ + GF_E_SP_32AR, /* Mult == SPLIT, w=32, Bad arg1/arg2 */ + GF_E_SP_32AS, /* Mult == SPLIT, w=32, ALTMAP requires SSE */ + GF_E_SP_32_A, /* Mult == SPLIT, w=32, ALTMAP only with 4/32 */ + GF_E_SP_32_S, /* Mult == SPLIT, w=32, SSE only with 4/32 */ + GF_E_SP_64AR, /* Mult == SPLIT, w=64, Bad arg1/arg2 */ + GF_E_SP_64AS, /* Mult == SPLIT, w=64, ALTMAP requires SSE */ + GF_E_SP_64_A, /* Mult == SPLIT, w=64, ALTMAP only with 4/64 */ + GF_E_SP_64_S, /* Mult == SPLIT, w=64, SSE only with 4/64 */ + GF_E_SP_8_AR, /* Mult == SPLIT, w=8, Bad arg1/arg2 */ + GF_E_SP_8__A, /* Mult == SPLIT, w=8, no ALTMAP */ + GF_E_SP_SSE3, /* Mult == SPLIT, Need SSSE3 for SSE */ + GF_E_COMP_A2, /* Mult == COMP, arg1 must be = 2 */ + GF_E_COMP_SS, /* Mult == COMP, SIMD|NOSIMD */ + GF_E_COMP__W, /* Mult == COMP, Bad w. */ + GF_E_UNKFLAG, /* Unknown flag in create_from.... */ + GF_E_UNKNOWN, /* Unknown mult_type. */ + GF_E_UNK_REG, /* Unknown region_type. */ + GF_E_UNK_DIV, /* Unknown divide_type. */ + GF_E_CFM___W, /* Mult == CFM, Bad w. */ + GF_E_CFM4POL, /* Mult == CFM & Prim Poly has high bits set. */ + GF_E_CFM8POL, /* Mult == CFM & Prim Poly has high bits set. */ + GF_E_CF16POL, /* Mult == CFM & Prim Poly has high bits set. */ + GF_E_CF32POL, /* Mult == CFM & Prim Poly has high bits set. */ + GF_E_CF64POL, /* Mult == CFM & Prim Poly has high bits set. */ + GF_E_FEWARGS, /* Too few args in argc/argv. */ + GF_E_BADPOLY, /* Bad primitive polynomial -- too many bits set. */ + GF_E_COMP_PP, /* Bad primitive polynomial -- bigger than sub-field. */ + GF_E_COMPXPP, /* Can't derive a default pp for composite field. */ + GF_E_BASE__W, /* Composite -- Base field is the wrong size. */ + GF_E_TWOMULT, /* In create_from... two -m's. */ + GF_E_TWO_DIV, /* In create_from... two -d's. */ + GF_E_POLYSPC, /* Bad numbera after -p. */ + GF_E_SPLITAR, /* Ran out of arguments in SPLIT */ + GF_E_SPLITNU, /* Arguments not integers in SPLIT. */ + GF_E_GROUPAR, /* Ran out of arguments in GROUP */ + GF_E_GROUPNU, /* Arguments not integers in GROUP. */ + GF_E_DEFAULT } gf_error_type_t; + diff --git a/IDA_new/gf-complete/include/gf_method.h b/IDA_new/gf-complete/include/gf_method.h new file mode 100644 index 0000000..880b349 --- /dev/null +++ b/IDA_new/gf-complete/include/gf_method.h @@ -0,0 +1,20 @@ +/* + * GF-Complete: A Comprehensive Open Source Library for Galois Field Arithmetic + * James S. Plank, Ethan L. Miller, Kevin M. Greenan, + * Benjamin A. Arnold, John A. Burnum, Adam W. Disney, Allen C. McBride. + * + * gf_method.h + * + * Parses argv to figure out the flags and arguments. Creates the gf. + */ + +#pragma once + +#include "gf_complete.h" + +/* Parses argv starting at "starting". + + Returns 0 on failure. + On success, it returns one past the last argument it read in argv. */ + +extern int create_gf_from_argv(gf_t *gf, int w, int argc, char **argv, int starting); diff --git a/IDA_new/gf-complete/include/gf_rand.h b/IDA_new/gf-complete/include/gf_rand.h new file mode 100644 index 0000000..24294ad --- /dev/null +++ b/IDA_new/gf-complete/include/gf_rand.h @@ -0,0 +1,22 @@ +/* + * GF-Complete: A Comprehensive Open Source Library for Galois Field Arithmetic + * James S. Plank, Ethan L. Miller, Kevin M. Greenan, + * Benjamin A. Arnold, John A. Burnum, Adam W. Disney, Allen C. McBride. + * + * gf_rand.h + * + * Random number generation, using the "Mother of All" random number generator. */ + +#pragma once +#include +#include +#include + +/* These are all pretty self-explanatory */ +uint32_t MOA_Random_32(); +uint64_t MOA_Random_64(); +void MOA_Random_128(uint64_t *x); +uint32_t MOA_Random_W(int w, int zero_ok); +void MOA_Fill_Random_Region (void *reg, int size); /* reg should be aligned to 4 bytes, but + size can be anything. */ +void MOA_Seed(uint32_t seed); diff --git a/IDA_new/gf-complete/include/gf_w16.h b/IDA_new/gf-complete/include/gf_w16.h new file mode 100644 index 0000000..fb4c0e9 --- /dev/null +++ b/IDA_new/gf-complete/include/gf_w16.h @@ -0,0 +1,66 @@ +/* + * GF-Complete: A Comprehensive Open Source Library for Galois Field Arithmetic + * James S. Plank, Ethan L. Miller, Kevin M. Greenan, + * Benjamin A. Arnold, John A. Burnum, Adam W. Disney, Allen C. McBride. + * + * gf_w16.h + * + * Defines and data structures for 16-bit Galois fields + */ + +#ifndef GF_COMPLETE_GF_W16_H +#define GF_COMPLETE_GF_W16_H + +#include + +#define GF_FIELD_WIDTH (16) +#define GF_FIELD_SIZE (1 << GF_FIELD_WIDTH) +#define GF_MULT_GROUP_SIZE GF_FIELD_SIZE-1 + +#define GF_BASE_FIELD_WIDTH (8) +#define GF_BASE_FIELD_SIZE (1 << GF_BASE_FIELD_WIDTH) + +struct gf_w16_logtable_data { + uint16_t log_tbl[GF_FIELD_SIZE]; + uint16_t antilog_tbl[GF_FIELD_SIZE * 2]; + uint16_t inv_tbl[GF_FIELD_SIZE]; + uint16_t *d_antilog; +}; + +struct gf_w16_zero_logtable_data { + int log_tbl[GF_FIELD_SIZE]; + uint16_t _antilog_tbl[GF_FIELD_SIZE * 4]; + uint16_t *antilog_tbl; + uint16_t inv_tbl[GF_FIELD_SIZE]; +}; + +struct gf_w16_lazytable_data { + uint16_t log_tbl[GF_FIELD_SIZE]; + uint16_t antilog_tbl[GF_FIELD_SIZE * 2]; + uint16_t inv_tbl[GF_FIELD_SIZE]; + uint16_t *d_antilog; + uint16_t lazytable[GF_FIELD_SIZE]; +}; + +struct gf_w16_bytwo_data { + uint64_t prim_poly; + uint64_t mask1; + uint64_t mask2; +}; + +struct gf_w16_split_8_8_data { + uint16_t tables[3][256][256]; +}; + +struct gf_w16_group_4_4_data { + uint16_t reduce[16]; + uint16_t shift[16]; +}; + +struct gf_w16_composite_data { + uint8_t *mult_table; +}; + +void gf_w16_neon_split_init(gf_t *gf); + +#endif /* GF_COMPLETE_GF_W16_H */ diff --git a/IDA_new/gf-complete/include/gf_w32.h b/IDA_new/gf-complete/include/gf_w32.h new file mode 100644 index 0000000..7734f30 --- /dev/null +++ b/IDA_new/gf-complete/include/gf_w32.h @@ -0,0 +1,71 @@ +/* + * GF-Complete: A Comprehensive Open Source Library for Galois Field Arithmetic + * James S. Plank, Ethan L. Miller, Kevin M. Greenan, + * Benjamin A. Arnold, John A. Burnum, Adam W. Disney, Allen C. McBride. + * + * gf_w32.h + * + * Defines and data structures for 32-bit Galois fields + */ + +#ifndef GF_COMPLETE_GF_W32_H +#define GF_COMPLETE_GF_W32_H + +#include + +#define GF_FIELD_WIDTH (32) +#define GF_FIRST_BIT ((gf_val_32_t)1 << 31) + +#define GF_BASE_FIELD_WIDTH (16) +#define GF_BASE_FIELD_SIZE (1 << GF_BASE_FIELD_WIDTH) +#define GF_BASE_FIELD_GROUP_SIZE GF_BASE_FIELD_SIZE-1 +#define GF_MULTBY_TWO(p) (((p) & GF_FIRST_BIT) ? (((p) << 1) ^ h->prim_poly) : (p) << 1) + +struct gf_split_2_32_lazy_data { + uint32_t tables[16][4]; + uint32_t last_value; +}; + +struct gf_w32_split_8_8_data { + uint32_t tables[7][256][256]; + uint32_t region_tables[4][256]; + uint32_t last_value; +}; + +struct gf_w32_group_data { + uint32_t *reduce; + uint32_t *shift; + int tshift; + uint64_t rmask; + uint32_t *memory; +}; + +struct gf_split_16_32_lazy_data { + uint32_t tables[2][(1<<16)]; + uint32_t last_value; +}; + +struct gf_split_8_32_lazy_data { + uint32_t tables[4][256]; + uint32_t last_value; +}; + +struct gf_split_4_32_lazy_data { + uint32_t tables[8][16]; + uint32_t last_value; +}; + +struct gf_w32_bytwo_data { + uint64_t prim_poly; + uint64_t mask1; + uint64_t mask2; +}; + +struct gf_w32_composite_data { + uint16_t *log; + uint16_t *alog; +}; + +void gf_w32_neon_split_init(gf_t *gf); + +#endif /* GF_COMPLETE_GF_W32_H */ diff --git a/IDA_new/gf-complete/include/gf_w4.h b/IDA_new/gf-complete/include/gf_w4.h new file mode 100644 index 0000000..8ee94a3 --- /dev/null +++ b/IDA_new/gf-complete/include/gf_w4.h @@ -0,0 +1,63 @@ +/* + * GF-Complete: A Comprehensive Open Source Library for Galois Field Arithmetic + * James S. Plank, Ethan L. Miller, Kevin M. Greenan, + * Benjamin A. Arnold, John A. Burnum, Adam W. Disney, Allen C. McBride. + * + * gf_w4.h + * + * Defines and data structures for 4-bit Galois fields + */ + +#ifndef GF_COMPLETE_GF_W4_H +#define GF_COMPLETE_GF_W4_H + +#include + +#define GF_FIELD_WIDTH 4 +#define GF_DOUBLE_WIDTH (GF_FIELD_WIDTH*2) +#define GF_FIELD_SIZE (1 << GF_FIELD_WIDTH) +#define GF_MULT_GROUP_SIZE (GF_FIELD_SIZE-1) + +/* ------------------------------------------------------------ + JSP: Each implementation has its own data, which is allocated + at one time as part of the handle. For that reason, it + shouldn't be hierarchical -- i.e. one should be able to + allocate it with one call to malloc. */ + +struct gf_logtable_data { + uint8_t log_tbl[GF_FIELD_SIZE]; + uint8_t antilog_tbl[GF_FIELD_SIZE * 2]; + uint8_t *antilog_tbl_div; +}; + +struct gf_single_table_data { + uint8_t mult[GF_FIELD_SIZE][GF_FIELD_SIZE]; + uint8_t div[GF_FIELD_SIZE][GF_FIELD_SIZE]; +}; + +struct gf_double_table_data { + uint8_t div[GF_FIELD_SIZE][GF_FIELD_SIZE]; + uint8_t mult[GF_FIELD_SIZE][GF_FIELD_SIZE*GF_FIELD_SIZE]; +}; +struct gf_quad_table_data { + uint8_t div[GF_FIELD_SIZE][GF_FIELD_SIZE]; + uint16_t mult[GF_FIELD_SIZE][(1<<16)]; +}; + +struct gf_quad_table_lazy_data { + uint8_t div[GF_FIELD_SIZE][GF_FIELD_SIZE]; + uint8_t smult[GF_FIELD_SIZE][GF_FIELD_SIZE]; + uint16_t mult[(1 << 16)]; +}; + +struct gf_bytwo_data { + uint64_t prim_poly; + uint64_t mask1; + uint64_t mask2; +}; + +// ARM NEON init functions +int gf_w4_neon_cfm_init(gf_t *gf); +void gf_w4_neon_single_table_init(gf_t *gf); + +#endif /* GF_COMPLETE_GF_W4_H */ diff --git a/IDA_new/gf-complete/include/gf_w64.h b/IDA_new/gf-complete/include/gf_w64.h new file mode 100644 index 0000000..9a74a81 --- /dev/null +++ b/IDA_new/gf-complete/include/gf_w64.h @@ -0,0 +1,50 @@ +/* + * GF-Complete: A Comprehensive Open Source Library for Galois Field Arithmetic + * James S. Plank, Ethan L. Miller, Kevin M. Greenan, + * Benjamin A. Arnold, John A. Burnum, Adam W. Disney, Allen C. McBride. + * + * gf_w64.h + * + * Defines and data structures for 64-bit Galois fields + */ + +#ifndef GF_COMPLETE_GF_W64_H +#define GF_COMPLETE_GF_W64_H + +#include + +#define GF_FIELD_WIDTH (64) +#define GF_FIRST_BIT (1ULL << 63) + +#define GF_BASE_FIELD_WIDTH (32) +#define GF_BASE_FIELD_SIZE (1ULL << GF_BASE_FIELD_WIDTH) +#define GF_BASE_FIELD_GROUP_SIZE GF_BASE_FIELD_SIZE-1 + +struct gf_w64_group_data { + uint64_t *reduce; + uint64_t *shift; + uint64_t *memory; +}; + +struct gf_split_4_64_lazy_data { + uint64_t tables[16][16]; + uint64_t last_value; +}; + +struct gf_split_8_64_lazy_data { + uint64_t tables[8][(1<<8)]; + uint64_t last_value; +}; + +struct gf_split_16_64_lazy_data { + uint64_t tables[4][(1<<16)]; + uint64_t last_value; +}; + +struct gf_split_8_8_data { + uint64_t tables[15][256][256]; +}; + +void gf_w64_neon_split_init(gf_t *gf); + +#endif /* GF_COMPLETE_GF_W64_H */ diff --git a/IDA_new/gf-complete/include/gf_w8.h b/IDA_new/gf-complete/include/gf_w8.h new file mode 100644 index 0000000..938fcfd --- /dev/null +++ b/IDA_new/gf-complete/include/gf_w8.h @@ -0,0 +1,99 @@ +/* + * GF-Complete: A Comprehensive Open Source Library for Galois Field Arithmetic + * James S. Plank, Ethan L. Miller, Kevin M. Greenan, + * Benjamin A. Arnold, John A. Burnum, Adam W. Disney, Allen C. McBride. + * + * gf_w8.c + * + * Defines and data stuctures for 8-bit Galois fields + */ + +#ifndef GF_COMPLETE_GF_W8_H +#define GF_COMPLETE_GF_W8_H + +#include "gf_int.h" +#include + +#define GF_FIELD_WIDTH (8) +#define GF_FIELD_SIZE (1 << GF_FIELD_WIDTH) +#define GF_HALF_SIZE (1 << (GF_FIELD_WIDTH/2)) +#define GF_MULT_GROUP_SIZE GF_FIELD_SIZE-1 + +#define GF_BASE_FIELD_WIDTH (4) +#define GF_BASE_FIELD_SIZE (1 << GF_BASE_FIELD_WIDTH) + +struct gf_w8_logtable_data { + uint8_t log_tbl[GF_FIELD_SIZE]; + uint8_t antilog_tbl[GF_FIELD_SIZE * 2]; + uint8_t inv_tbl[GF_FIELD_SIZE]; +}; + +struct gf_w8_logzero_table_data { + short log_tbl[GF_FIELD_SIZE]; /* Make this signed, so that we can divide easily */ + uint8_t antilog_tbl[512+512+1]; + uint8_t *div_tbl; + uint8_t *inv_tbl; +}; + +struct gf_w8_logzero_small_table_data { + short log_tbl[GF_FIELD_SIZE]; /* Make this signed, so that we can divide easily */ + uint8_t antilog_tbl[255*3]; + uint8_t inv_tbl[GF_FIELD_SIZE]; + uint8_t *div_tbl; +}; + +struct gf_w8_composite_data { + uint8_t *mult_table; +}; + +/* Don't change the order of these relative to gf_w8_half_table_data */ + +struct gf_w8_default_data { + uint8_t high[GF_FIELD_SIZE][GF_HALF_SIZE]; + uint8_t low[GF_FIELD_SIZE][GF_HALF_SIZE]; + uint8_t divtable[GF_FIELD_SIZE][GF_FIELD_SIZE]; + uint8_t multtable[GF_FIELD_SIZE][GF_FIELD_SIZE]; +}; + +struct gf_w8_half_table_data { + uint8_t high[GF_FIELD_SIZE][GF_HALF_SIZE]; + uint8_t low[GF_FIELD_SIZE][GF_HALF_SIZE]; +}; + +struct gf_w8_single_table_data { + uint8_t divtable[GF_FIELD_SIZE][GF_FIELD_SIZE]; + uint8_t multtable[GF_FIELD_SIZE][GF_FIELD_SIZE]; +}; + +struct gf_w8_double_table_data { + uint8_t div[GF_FIELD_SIZE][GF_FIELD_SIZE]; + uint16_t mult[GF_FIELD_SIZE][GF_FIELD_SIZE*GF_FIELD_SIZE]; +}; + +struct gf_w8_double_table_lazy_data { + uint8_t div[GF_FIELD_SIZE][GF_FIELD_SIZE]; + uint8_t smult[GF_FIELD_SIZE][GF_FIELD_SIZE]; + uint16_t mult[GF_FIELD_SIZE*GF_FIELD_SIZE]; +}; + +struct gf_w4_logtable_data { + uint8_t log_tbl[GF_BASE_FIELD_SIZE]; + uint8_t antilog_tbl[GF_BASE_FIELD_SIZE * 2]; + uint8_t *antilog_tbl_div; +}; + +struct gf_w4_single_table_data { + uint8_t div[GF_BASE_FIELD_SIZE][GF_BASE_FIELD_SIZE]; + uint8_t mult[GF_BASE_FIELD_SIZE][GF_BASE_FIELD_SIZE]; +}; + +struct gf_w8_bytwo_data { + uint64_t prim_poly; + uint64_t mask1; + uint64_t mask2; +}; + +int gf_w8_neon_cfm_init(gf_t *gf); +void gf_w8_neon_split_init(gf_t *gf); + +#endif /* GF_COMPLETE_GF_W8_H */ diff --git a/IDA_new/gf-complete/m4/ax_check_compile_flag.m4 b/IDA_new/gf-complete/m4/ax_check_compile_flag.m4 new file mode 100644 index 0000000..c3a8d69 --- /dev/null +++ b/IDA_new/gf-complete/m4/ax_check_compile_flag.m4 @@ -0,0 +1,72 @@ +# =========================================================================== +# http://www.gnu.org/software/autoconf-archive/ax_check_compile_flag.html +# =========================================================================== +# +# SYNOPSIS +# +# AX_CHECK_COMPILE_FLAG(FLAG, [ACTION-SUCCESS], [ACTION-FAILURE], [EXTRA-FLAGS]) +# +# DESCRIPTION +# +# Check whether the given FLAG works with the current language's compiler +# or gives an error. (Warnings, however, are ignored) +# +# ACTION-SUCCESS/ACTION-FAILURE are shell commands to execute on +# success/failure. +# +# If EXTRA-FLAGS is defined, it is added to the current language's default +# flags (e.g. CFLAGS) when the check is done. The check is thus made with +# the flags: "CFLAGS EXTRA-FLAGS FLAG". This can for example be used to +# force the compiler to issue an error when a bad flag is given. +# +# NOTE: Implementation based on AX_CFLAGS_GCC_OPTION. Please keep this +# macro in sync with AX_CHECK_{PREPROC,LINK}_FLAG. +# +# LICENSE +# +# Copyright (c) 2008 Guido U. Draheim +# Copyright (c) 2011 Maarten Bosmans +# +# 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 . +# +# As a special exception, the respective Autoconf Macro's copyright owner +# gives unlimited permission to copy, distribute and modify the configure +# scripts that are the output of Autoconf when processing the Macro. You +# need not follow the terms of the GNU General Public License when using +# or distributing such scripts, even though portions of the text of the +# Macro appear in them. The GNU General Public License (GPL) does govern +# all other use of the material that constitutes the Autoconf Macro. +# +# This special exception to the GPL applies to versions of the Autoconf +# Macro released by the Autoconf Archive. When you make and distribute a +# modified version of the Autoconf Macro, you may extend this special +# exception to the GPL to apply to your modified version as well. + +#serial 2 + +AC_DEFUN([AX_CHECK_COMPILE_FLAG], +[AC_PREREQ(2.59)dnl for _AC_LANG_PREFIX +AS_VAR_PUSHDEF([CACHEVAR],[ax_cv_check_[]_AC_LANG_ABBREV[]flags_$4_$1])dnl +AC_CACHE_CHECK([whether _AC_LANG compiler accepts $1], CACHEVAR, [ + ax_check_save_flags=$[]_AC_LANG_PREFIX[]FLAGS + _AC_LANG_PREFIX[]FLAGS="$[]_AC_LANG_PREFIX[]FLAGS $4 $1" + AC_COMPILE_IFELSE([AC_LANG_PROGRAM()], + [AS_VAR_SET(CACHEVAR,[yes])], + [AS_VAR_SET(CACHEVAR,[no])]) + _AC_LANG_PREFIX[]FLAGS=$ax_check_save_flags]) +AS_IF([test x"AS_VAR_GET(CACHEVAR)" = xyes], + [m4_default([$2], :)], + [m4_default([$3], :)]) +AS_VAR_POPDEF([CACHEVAR])dnl +])dnl AX_CHECK_COMPILE_FLAGS diff --git a/IDA_new/gf-complete/m4/ax_ext.m4 b/IDA_new/gf-complete/m4/ax_ext.m4 new file mode 100644 index 0000000..95c4dbe --- /dev/null +++ b/IDA_new/gf-complete/m4/ax_ext.m4 @@ -0,0 +1,75 @@ +# +# This macro is based on http://www.gnu.org/software/autoconf-archive/ax_ext.html +# but simplified to do compile time SIMD checks only +# + +AC_DEFUN([AX_EXT], +[ + AC_REQUIRE([AC_CANONICAL_HOST]) + + case $host_cpu in + aarch64*) + AC_DEFINE(HAVE_ARCH_AARCH64,,[targeting AArch64]) + SIMD_FLAGS="$SIMD_FLAGS -DARCH_AARCH64" + + AC_CACHE_CHECK([whether NEON is enabled], [ax_cv_have_neon_ext], [ax_cv_have_neon_ext=yes]) + if test "$ax_cv_have_neon_ext" = yes; then + AX_CHECK_COMPILE_FLAG(-march=armv8-a+simd, [SIMD_FLAGS="$SIMD_FLAGS -march=armv8-a+simd -DARM_NEON"], [ax_cv_have_neon_ext=no]) + fi + ;; + + arm*) + AC_CACHE_CHECK([whether NEON is enabled], [ax_cv_have_neon_ext], [ax_cv_have_neon_ext=yes]) + if test "$ax_cv_have_neon_ext" = yes; then + AX_CHECK_COMPILE_FLAG(-mfpu=neon, [SIMD_FLAGS="$SIMD_FLAGS -mfpu=neon -DARM_NEON"], [ax_cv_have_neon_ext=no]) + fi + ;; + + powerpc*) + AC_CACHE_CHECK([whether altivec is enabled], [ax_cv_have_altivec_ext], [ax_cv_have_altivec_ext=yes]) + if test "$ax_cv_have_altivec_ext" = yes; then + AX_CHECK_COMPILE_FLAG(-faltivec, [SIMD_FLAGS="$SIMD_FLAGS -faltivec"], [ax_cv_have_altivec_ext=no]) + fi + ;; + + i[[3456]]86*|x86_64*|amd64*) + + AC_CACHE_CHECK([whether sse is enabled], [ax_cv_have_sse_ext], [ax_cv_have_sse_ext=yes]) + if test "$ax_cv_have_sse_ext" = yes; then + AX_CHECK_COMPILE_FLAG(-msse, [SIMD_FLAGS="$SIMD_FLAGS -msse -DINTEL_SSE"], [ax_cv_have_sse_ext=no]) + fi + + AC_CACHE_CHECK([whether sse2 is enabled], [ax_cv_have_sse2_ext], [ax_cv_have_sse2_ext=yes]) + if test "$ax_cv_have_sse2_ext" = yes; then + AX_CHECK_COMPILE_FLAG(-msse2, [SIMD_FLAGS="$SIMD_FLAGS -msse2 -DINTEL_SSE2"], [ax_cv_have_sse2_ext=no]) + fi + + AC_CACHE_CHECK([whether sse3 is enabled], [ax_cv_have_sse3_ext], [ax_cv_have_sse3_ext=yes]) + if test "$ax_cv_have_sse3_ext" = yes; then + AX_CHECK_COMPILE_FLAG(-msse3, [SIMD_FLAGS="$SIMD_FLAGS -msse3 -DINTEL_SSE3"], [ax_cv_have_sse3_ext=no]) + fi + + AC_CACHE_CHECK([whether ssse3 is enabled], [ax_cv_have_ssse3_ext], [ax_cv_have_ssse3_ext=yes]) + if test "$ax_cv_have_ssse3_ext" = yes; then + AX_CHECK_COMPILE_FLAG(-mssse3, [SIMD_FLAGS="$SIMD_FLAGS -mssse3 -DINTEL_SSSE3"], [ax_cv_have_ssse3_ext=no]) + fi + + AC_CACHE_CHECK([whether pclmuldq is enabled], [ax_cv_have_pclmuldq_ext], [ax_cv_have_pclmuldq_ext=yes]) + if test "$ax_cv_have_pclmuldq_ext" = yes; then + AX_CHECK_COMPILE_FLAG(-mpclmul, [SIMD_FLAGS="$SIMD_FLAGS -mpclmul -DINTEL_SSE4_PCLMUL"], [ax_cv_have_pclmuldq_ext=no]) + fi + + AC_CACHE_CHECK([whether sse4.1 is enabled], [ax_cv_have_sse41_ext], [ax_cv_have_sse41_ext=yes]) + if test "$ax_cv_have_sse41_ext" = yes; then + AX_CHECK_COMPILE_FLAG(-msse4.1, [SIMD_FLAGS="$SIMD_FLAGS -msse4.1 -DINTEL_SSE4"], [ax_cv_have_sse41_ext=no]) + fi + + AC_CACHE_CHECK([whether sse4.2 is enabled], [ax_cv_have_sse42_ext], [ax_cv_have_sse42_ext=yes]) + if test "$ax_cv_have_sse42_ext" = yes; then + AX_CHECK_COMPILE_FLAG(-msse4.2, [SIMD_FLAGS="$SIMD_FLAGS -msse4.2 -DINTEL_SSE4"], [ax_cv_have_sse42_ext=no]) + fi + ;; + esac + + AC_SUBST(SIMD_FLAGS) +]) diff --git a/IDA_new/gf-complete/manual/gf-complete.html b/IDA_new/gf-complete/manual/gf-complete.html new file mode 100644 index 0000000..ed79e25 --- /dev/null +++ b/IDA_new/gf-complete/manual/gf-complete.html @@ -0,0 +1,3484 @@ + + + + + + + + + + +
+ +

+GF-Complete: A Comprehensive Open Source Library for Galois
+Field Arithmetic +

+ +

Version 1.02

+ +

James S. Plank*        Ethan L. Miller +Kevin M. Greenan        Benjamin A. Arnold
+John A. Burnum        Adam W. Disney        +Allen C. McBride + +


+ + + + + +https://bitbucket.org/jimplank/gf-complete + +

+ +http://web.eecs.utk.edu/~plank/plank/papers/GF-Complete-Manual-1.02.pdf + + +

+ + + + + + + +
+ + +
+ +This is a user's manual for GF-Complete, version 1.02. This release supersedes version 0.1 and represents the first +major release of GF-Complete. To our knowledge, this library implements every Galois Field multiplication technique +applicable to erasure coding for storage, which is why we named it GF-Complete. The primary goal of this library is +to allow storage system researchers and implementors to utilize very fast Galois Field arithmetic for Reed-Solomon +coding and the like in their storage installations. The secondary goal is to allow those who want to explore different +ways to perform Galois Field arithmetic to be able to do so effectively. + + +

+If you wish to cite GF-Complete, please cite technical report UT-CS-13-716: [PMG+13]. + +

+ + +

If You Use This Library or Document

+ + + +Please send me an email to let me know how it goes. Or send me an email just to let me know you are using the +library. One of the ways in which we are evaluated both internally and externally is by the impact of our work, and if +you have found this library and/or this document useful, we would like to be able to document it. Please send mail to +plank@cs.utk.edu. Please send bug reports to that address as well. + + + +

+The library itself is protected by the New BSD License. It is free to use and modify within the bounds of this +license. To the authors' knowledge, none of the techniques implemented in this library have been patented, and the +authors are not pursing patents.


+ +
+ + +Finding the Code +

+This code is actively maintained on bitbucket: https://bitbucket.org/jimplank/gf-complete. There are +previous versions on my UTK site as a technical report; however, that it too hard to maintain, so the main version is +on bitbucket.

+ + +Two Related Papers

+ +This software acccompanies a large paper that describes these implementation techniques in detail [PGM13a]. We +will refer to this as "The Paper." You do not have to read The Paper to use the software. However, if you want to +start exploring the various implementations, then The Paper is where you'll want to go to learn about the techniques +in detail. + + + +

This library implements the techniques described in the paper "Screaming Fast Galois Field Arithmetic Using Intel +SIMD Instructions," [PGM13b]. The Paper describes all of those techniques as well. +



+ +If You Would Like HelpWith the Software

+ +Please contact the first author of this manual.

+ +Changes from Revision 1.01 +

+The major change is that we are using autoconf to aid with compilation, thus obviating the need for the old flag_tester +code. Additionally, we have added a quick timing tool, and we have modified gf_methods so that it may be used to +run the timing tool and the unit tester. + + + + + + + + + + + + + + + + + + +
+CONTENT 3 +

Contents

+
+1 Introduction 5 +

+2 Files in the Library 6
+ +
+2.1 Header files in the directory "include" . .. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 6
+2.2 Source files in the "src" directory . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 7
+2.3 Library tools files in the "tools" directory . . . . . . . . . . .. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 7
+2.4 The unit tester in the "test" directory. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 8
+2.5 Example programs in the "examples" directory . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 8 + +
+
+
+ +3 Compilation 8

+4 Some Tools and Examples to Get You Started 8

+ + + +
+4.1 Three Simple Command Line Tools: gf_mult, gf_div and gf_add . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 8
+4.2 Quick Starting Example #1: Simple multiplication and division . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 9
+4.3 Quick Starting Example #2: Multiplying a region by a constant . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 10
+4.4 Quick Starting Example #3: Using w = 64 . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 11
+4.5 Quick Starting Example #4: Using w = 128. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 11 +
+
+ + +
+5 Important Information on Alignment when Multiplying Regions 12

+ +6 The Defaults 13
+ +
+ +
+6.1 Changing the Defaults . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 14
+ + +
    +
  • 6.1.1 Changing the Components of a Galois Field with create_gf_from_argv() . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 15
    +
  • +
  • +6.1.2 Changing the Polynomial. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 16
    +
  • +
  • +6.1.3 Changing the Multiplication Technique. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 17 +
  • + + +
  • +6.1.4 Changing the Division Technique . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 19 +
  • + + +
  • +6.1.5 Changing the Region Technique. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .. 19 +
  • +
+6.2 Determining Supported Techniques with gf_methods . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 20
+ +6.3 Testing with gf_unit, gf_time, and time_tool.sh . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 21 + +
    +
  • +6.3.1 time_tool.sh . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .. . . . . . . . 22 +
  • + +
  • +6.3.2 An example of gf_methods and time_tool.sh . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .. . . . . . . . . .. . . 23 +
  • + +
+ +6.4 Calling gf_init_hard() . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .. . . . . . . . .. . . . . . . . .. . . . . . . . .. . . . . . . . . . . 24
+ +6.5 gf_size() . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .. . . . . . . . .. . . . . . . . .. . . . . . . . .. . . . . . . . .. . . . . . . . . . .. . 26

+
+ + +
+8 Further Information on Options and Algorithms 26


+
+7.1 Inlining Single Multiplication and Division for Speed . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 26
+7.2 Using different techniques for single and region multiplication . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 27
+7.3 General w . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 28
+ +7.4 Arguments to "SPLIT" . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 28
+7.5 Arguments to "GROUP" . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 29
+7.6 Considerations with "COMPOSITE" . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 30
+7.7 "CARRY_FREE" and the Primitive Polynomial . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 31
+7.8 More on Primitive Polynomials . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .. . . . . . . . . . 31
+ + +
    +
  • +7.8.1 Primitive Polynomials that are not Primitive . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 31
    + +
  • +
  • 7.8.2 Default Polynomials for Composite Fields . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 32
    + +
  • +
+ +
+ + + + + + + + + + + +
+CONTENT 4 + +
+
    +
  • 7.8.3 The Program gf_poly for Verifying Irreducibility of Polynomials 33 +
  • +
+ + +7.9"ALTMAP" considerations and extract_word() 34 +
    +
  • + +7.9.1 Alternate mappings with "SPLIT" . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 34
    +
  • +
  • +7.9.2 Alternate mappings with "COMPOSITE" . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 36
    +
  • +
  • +7.9.3 The mapping of "CAUCHY" . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .. . . . . .. . . . . . . . . . . .. 37
    +
  • +
+
+ + +8 Thread Safety 37

+ +9 Listing of Procedures 37

+ +10 Troubleshooting 38

+11 Timings 41

+ +
+11.1 Multiply() . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .. . . . . . . . .. . . . . . . . .. . . . . . . . .. . . . . . . . . . . . . . . . .. . . . 42
+11.2 Divide() . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .. . . . . . . . .. . . . . . . . .. . . . . . . . .. . . . . . . . . . . .. . . . . 42
+11.3 Multiply Region() . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .. . . . . . . . .. . . . . . . . .. . . . . . . . . . . . . . . . . . 43
+
+ + + + + + +
+INTRODUCTION 5 + + +

1 Introduction

+ +Galois Field arithmetic forms the backbone of erasure-coded storage systems, most famously the Reed-Solomon +erasure code. A Galois Field is defined over w-bit words and is termed GF(2w). As such, the elements of a Galois +Field are the integers 0, 1, . . ., 2w - 1. Galois Field arithmetic defines addition and multiplication over these closed +sets of integers in such a way that they work as you would hope they would work. Specifically, every number has a +unique multiplicative inverse. Moreover, there is a value, typically the value 2, which has the property that you can +enumerate all of the non-zero elements of the field by taking that value to successively higher powers. + + +

Addition in a Galois Field is equal to the bitwise exclusive-or operation. That's nice and convenient. Multiplication +is a little more complex, and there are many, many ways to implement it. The Paper describes them all, and the +following references providemore supporting material: [Anv09, GMS08, LHy08, LD00, LBOX12, Pla97]. The intent +of this library is to implement all of the techniques. That way, their performancemay be compared, and their tradeoffs +may be analyzed.

+ + + + +

    + +When used for erasure codes, there are typically five important operations:
    +
  1. Adding two numbers in GF(2w). That's bitwise exclusive-or.
  2. +
  3. Multiplying two numbers in GF(2w). Erasure codes are usually based on matrices in GF(2w), and constructing +these matrices requires both addition and multiplication.
  4. +
  5. Dividing two numbers in GF(2w). Sometimes you need to divide to construct matrices (for example, Cauchy +Reed-Solomon codes [BKK+95, Rab89]). More often, though, you use division to invert matrices for decoding. +Sometimes it is easier to find a number's inverse than it is to divide. In that case, you can divide by multiplying +by an inverse.
  6. + +
  7. adding two regions of numbers in GF(2w), which will be explained along with...
  8. +
  9. Mutiplying a region of numbers in GF(2w) by a constant in GF(2w). Erasure coding typically boils down +to performing dot products in GF(2w). For example, you may define a coding disk using the equation:

  10. + + + + +
    c0= d0 + 2d1 + 4d2 + 8d3.

    + +That looks like three multiplications and three additions However, the way ' implemented in a disk system +looks as in Figure 1. Large regions of disks are partitioned into w-bit words in GF(2w). In the example, let us +suppose that w = 8, and therefore that words are bytes. Then the regions pictured are 1 KB from each disk. +The bytes on disk Di are labeled di,0, di,1, . . . , di,1023, and the equation above is replicated 1024 times. For +0 ≤ j < 1024: +

    +
    c0,j = d0,j + 2d1,j + 4d2,j + 8d3,j .
    +
    + + +While it's possible to implement each of these 1024 equations independently, using the single multiplication +and addition operations above, it is often much more efficient to aggregate. For example, most computer architectures +support bitwise exclusive-or of 64 and 128 bit words. Thus, it makes much more sense to add regions +of numbers in 64 or 128 bit chunks rather than as words in GF(2w). Multiplying a region by a constant can +leverage similar optimizations.
+ + +

GF-Complete supports multiplication and division of single values for all values of w ≤ 32, plus w = 64 and w = +128. It also supports adding two regions of memory (for any value of w, since addition equals XOR), and multiplying +a region by a constant in GF(24), GF(28), GF(216), GF(232), GF(264) and GF(2128). These values are chosen +because words in GF(2w) fit into machine words with these values of w. Other values of w don't lend themselves +to efficient multiplication of regions by constants (although see the "CAUCHY" option in section 6.1.5 for a way to +multiply regions for other values of w).

+ + + + + + +
+ +2     FILES IN THE LIBRARY 6


+ + + +



+ +Figure 1: An example of adding two regions of numbers, and multiplying a region of numbers by a constant +in GF(2w) . In this example, w = 8, and each disk is holding a 1KB region. The same coding equation - +c0,j = d0,j + ad1,j + a2d2,j + a3d3,j is applied 1024 times. However, rather than executing this equation 1024 +times, it is more efficient to implement this with three region-constant multiplications and three region-region additions. + +

2     Files in the Library

+This section provides an overview of the files that compose GF-Complete. They are partitioned among multiple +directories. + +

2.1     Header files in the directory "include"

+ +The following header files are part of GF-Complete. +
    +
  • gf_complete.h: This is the header file that applications should include. It defines the gf_t type, which holds +all of the data that you need to perform the various operations in GF(2w). It also defines all of the arithmetic +operations. For an application to use this library, you should include gf_complete.h and then compile with the +library src/libgf_complete.la.

  • + +
  • gf_method.h: If you are wanting to modify the implementation techniques from the defaults, this file provides +a "helper" function so that you can do it from the Unix command line. +

  • + +
  • gf_general.h: This file has helper routines for doing basic Galois Field operations with any legal value of w. +The problem is that w ≤ 32, w = 64 and w = 128 all have different data types, which is a pain. The procedures +in this file try to alleviate that pain. They are used in gf_mult, gf_unit and gf_time. I'm guessing that most +applications won't use them, as most applications use w ≤ 32.

  • + +
  • gf_rand.h: I've learned that srand48() and its kin are not supported in all C installations. Therefore, this file +defines some randomnumber generators to help test the programs. The randomnumber generator is the "Mother +
  • + +
+ + + + + + + +
+ +2     FILES IN THE LIBRARY 7


+
    + +of All" random number generator [Mar94] which we've selected because it has no patent issues. gf_unit and +gf_time use these random number generators.

    +
  • gf_int.h: This is an internal header file that the various source files use. This is not intended for applications to +include.

  • +
  • config.xx and stamp-h1 are created by autoconf, and should be ignored by applications.
  • +
+ +

2.2     Source files in the "src" directory"

+
    +The following C files compose gf_complete.a, and they are in the direcoty src. You shouldn't have to mess with these +files, but we include them in case you have to:

    +
  • gf_.c: This implements all of the procedures in both gf_complete.h and gf_int.h.

  • +
  • gf_w4.c: Procedures specific to w = 4.

  • +
  • gf_w8.c: Procedures specific to w = 8

  • +
  • gf_w16.c: Procedures specific to w = 16

  • +
  • gf_w32.c: Procedures specific to w = 32

  • +
  • gf_w64.c: Procedures specific to w = 64

  • +
  • gf_w128.c: Procedures specific to w = 128

  • +
  • gf_wgen.c: Procedures specific to other values of w between 1 and 31

  • +
  • gf_general.c: Procedures that let you manipulate general values, regardless of whether w ≤ 32, w = 64 +or w = 128. (I.e. the procedures defined in gf_ general.h)

  • +
  • gf_method.c: Procedures to help you switch between the various implementation techniques. (I.e. the procedures +defined in gf_method.h)

  • +
  • gf_ rand.c:"The Mother of all" random number generator. (I.e. the procedures defined in gf_rand.h)

+ +

2.3     Library tools files in the "tools" directory

+ +
    +The following are tools to help you with Galois Field arithmetic, and with the library. They are explained in greater +detail elsewhere in this manual.

    +
  • gf_mult.c, gf_ div.c and gf_ add: Command line tools to do multiplication, division and addition by single numbers

  • +
  • gf_time.c: A program that times the procedures for given values of w and implementation options

  • +
  • time_tool.sh: A shell script that helps perform rough timings of the various multiplication, division and region +operations in GF-Complete

  • +
  • gf_methods.c: A program that enumerates most of the implementation methods supported by GF-Complete

  • +
  • gf_poly.c: A program to identify irreducible polynomials in regular and composite Galois Fields

  • + +
+ + + + + + + + +
+ +3     COMPILATION 8


+ + +

2.4     The unit tester in the "test" directory

+ +The test directory contains the proram gf_unit.c, which performs a battery of unit tests on GF-Complete. This is +explained in more detail in section 6.3. + + +

2.5    Example programs in the "examples" directory

+ +There are seven example programs to help you understand various facets of GF-Complete. They are in the files +gf_example x.c in the examples directory. They are explained in sections 4.2 through 4.5, and section 7.9.

+ +

3     Compilation

+ +From revision 1.02 forward, we are using autoconf. The old "flag tester" directory is now gone, as it is no longer in +use.

+To compile and install, you should do the standard operations that you do with most open source Unix code:

+ +UNIX> ./configure
+...
+UNIX> make
+...
+UNIX> sudo make install

+ + +

If you perform the install, then the header, source, tool, and library files will be moved to system locations. In +particular, you may then compile the library by linking with the flag -lgf_complete, and you may use the tools from a +global executable directory (like /usr/local/bin).

+ +

+If you don't perform the install, then the header and tool files will be in their respective directories, and the library +will be in src/libgf_complete.la.

+

+If your system supports the various Intel SIMD instructions, the compiler will find them, and GF-Complete will +use them by default.

+ + + +

4     Some Tools and Examples to Get You Started

+

4.1 Three Simple Command Line Tools: gf_mult, gf_div and gf_add

+ + +Before delving into the library, it may be helpful to explore Galois Field arithmetic with the command line tools: +gf_mult, gf_div and gf_add. These perform multiplication, division and addition on elements in GF(2w). If these are +not installed on your system, then you may find them in the tools directory. Their syntax is: +
    +
  • gf_mult a b w - Multiplies a and b in GF(2w).

  • +
  • gf_div a b w - Divides a by b in GF(2w ).

  • +
  • gf_add a b w - Adds a and b in GF(2w ).

  • + +You may use any value of w from 1 to 32, plus 64 and 128. By default, the values are read and printed in decimal; +however, if you append an 'h' to w , then a, b and the result will be printed in hexadecimal. For w = 128, the 'h' is +mandatory, and all values will be printed in hexadecimal. + + + + + + + +
    + +4     SOME TOOLS AND EXAMPLES TO GET YOU STARTED 9 9


    + + +

    Try them out on some examples like the ones below. You of course don't need to know that, for example, 5 * 4 = 7 +in GF(24 ) ; however, once you know that, you know that 7/ +5 = 4 and 7/4 = 5. You should be able to verify the gf_add +statements below in your head. As for the other gf_mult's, you can simply verify that division and multiplication work +with each other as you hope they would.

    +

    +
    + +UNIX> gf_mult 5 4 4
    +7
    +UNIX> gf_div 7 5 4
    +4
    +UNIX> gf_div 7 4 4
    +5
    +UNIX> gf_mult 8000 2 16h
    +100b
    +UNIX> gf_add f0f0f0f0f0f0f0f0 1313131313131313 64h
    +e3e3e3e3e3e3e3e3
    +UNIX> gf_mult f0f0f0f0f0f0f0f0 1313131313131313 64h
    +8da08da08da08da0
    +UNIX> gf_div 8da08da08da08da0 1313131313131313 64h
    +f0f0f0f0f0f0f0f0
    +UNIX> gf_add f0f0f0f0f0f0f0f01313131313131313 1313131313131313f0f0f0f0f0f0f0f0 128h
    +e3e3e3e3e3e3e3e3e3e3e3e3e3e3e3e3
    +UNIX> gf_mult f0f0f0f0f0f0f0f01313131313131313 1313131313131313f0f0f0f0f0f0f0f0 128h
    +786278627862784982d782d782d7816e
    +UNIX> gf_div 786278627862784982d782d782d7816e f0f0f0f0f0f0f0f01313131313131313 128h
    +1313131313131313f0f0f0f0f0f0f0f0
    +UNIX>

    + +
    + + +Don't bother trying to read the source code of these programs yet. Start with some simpler examples like the ones +below.

    + +

    4.2 Quick Starting Example #1: Simple multiplication and division

    + +The source files for these examples are in the examples directory. +

    These two examples are intended for those who just want to use the library without getting too complex. The +first example is gf_example 1, and it takes one command line argument - w, which must be between 1 and 32. It +generates two random non-zero numbers in GF(2w ) and multiplies them. After doing that, it divides the product by +each number.

    +

    +To perform multiplication and division in GF(2w ) , you must declare an instance of the gf_t type, and then initialize +it for GF(2w ) by calling gf_init_easy(). This is done in gf_example 1.c with the following lines:



    + +gf_t gf;

    r +...

    +if (!gf_init_easy(&gf, w)) {
    +fprintf(stderr, "Couldn't initialize GF structure.\n");
    +exit(0);
    +}
    + + + + + + +
    + +4     SOME TOOLS AND EXAMPLES TO GET YOU STARTED 10


    + +

    Once gf is initialized, you may use it for multiplication and division with the function pointers multiply.w32 and +divide.w32. These work for any element of GF(2w) so long as w ≤ 32.



    + +
    +
    +c = gf.multiply.w32(&gf, a, b);
    +printf("%u * %u = %u\n", a, b, c);

    +printf("%u / %u = %u\n", c, a, gf.divide.w32(&gf, c, a));
    +printf("%u / %u = %u\n", c, b, gf.divide.w32(&gf, c, b));
    + + +
    +

    +Go ahead and test this program out. You can use gf_mult and gf_div to verify the results:

    + +
    +UNIX> gf_example_1 4
    +12 * 4 = 5
    +5 / 12 = 4
    +5 / 4 = 12
    +UNIX> gf_mult 12 4 4
    +5
    +UNIX> gf_example_1 16
    +14411 * 60911 = 44568
    +44568 / 14411 = 60911
    +44568 / 60911 = 14411
    +UNIX> gf_mult 14411 60911 16
    +44568
    +UNIX>

    +
    + +gf_init_easy() (and later_gf_init_hard()) do call malloc() to implement internal structures. To release memory, call +gf_free(). Please see section 6.4 to see how to call gf_init_hard() in such a way that it doesn't call malloc().

    + + + +

    4.3      Quick Starting Example #2: Multiplying a region by a constant

    + + +The program gf_example 2 expands on gf_example 1. If w is equal to 4, 8, 16 or 32, it performs a region multiply +operation. It allocates two sixteen byte regions, r1 and r2, and then multiples r1 by a and puts the result in r2 using +the multiply_region.w32 function pointer:

    + +
    +gf.multiply_region.w32 (&gf, r1, r2, a, 16, 0);

    +
    + +That last argument specifies whether to simply place the product into r2 or to XOR it with the contents that are already +in r2. Zero means to place the product there. When we run it, it prints the results of the multiply_region.w32 in +hexadecimal. Again, you can verify it using gf_mult:

    +
    +UNIX> gf_example_2 4
    +12 * 2 = 11
    +11 / 12 = 2
    +11 / 2 = 12

    +multiply_region by 0xc (12)

    +R1 (the source): 0 2 d 9 d 6 8 a 8 d b 3 5 c 1 8 8 e b 0 6 1 5 a 2 c 4 b 3 9 3 6
    +R2 (the product): 0 b 3 6 3 e a 1 a 3 d 7 9 f c a a 4 d 0 e c 9 1 b f 5 d 7 6 7 e
    + +
    + + + + + + + + + + +
    + +4     SOME TOOLS AND EXAMPLES TO GET YOU STARTED 11


    + +
    + + + + + + + + +
    UNIX> gf_example_2 16
    49598 * 35999 = 19867
    19867 / 49598 = 35999
    19867 / 35999 = 49598

    + + +  multiply_region by 0xc1be (49598)

    + + + + + + +
    R1 (the source): 8c9f b30e 5bf3 7cbb 16a9 105d 9368 4bbe
    R2 (the product): 4d9b 992d 02f2 c95c 228e ec82 324e 35e4
    +
    +
    +
    +UNIX> gf_mult c1be 8c9f 16h
    +4d9b
    +UNIX> gf_mult c1be b30e 16h
    +992d
    +UNIX>

    +
    +
    + +

    4.4       Quick Starting Example #3: Using w = 64

    +The program in gf_example 3.c is identical to the previous program, except it uses GF(264 ). Now a, b and c are +uint64 t's, and you have to use the function pointers that have w64 extensions so that the larger types may be employed. +

    +
    + +UNIX> gf_example_31 + + + + + + + +
    a9af3adef0d23242 * 61fd8433b25fe7cd = bf5acdde4c41ee0c
    bf5acdde4c41ee0c / a9af3adef0d23242 = 61fd8433b25fe7cd
    bf5acdde4c41ee0c / 61fd8433b25fe7cd = a9af3adef0d23242


    + +  multiply_region by a9af3adef0d23242

    + + + + +
    R1 (the source): 61fd8433b25fe7cd 272d5d4b19ca44b7 3870bf7e63c3451a 08992149b3e2f8b7
    R2 (the product): bf5acdde4c41ee0c ad2d786c6e4d66b7 43a7d857503fd261 d3d29c7be46b1f7c
    + +
    + +UNIX> gf_mult a9af3adef0d23242 61fd8433b25fe7cd 64h
    +bf5acdde4c41ee0c
    +UNIX>

    +
    +
    +

    4.5       Quick Starting Example #4: Using w = 128

    +Finally, the program in gf_example_4.c uses GF(2128). Since there is not universal support for uint128 t, the library +represents 128-bit numbers as arrays of two uint64 t's. The function pointers for multiplication, division and region +multiplication now accept the return values as arguments:

    + +gf.multiply.w128(&gf, a, b, c);

    + +Again, we can use gf_mult and gf_div to verify the results:

    +
    +
    +UNIX> gf_example_4
    + + + + + + +
    e252d9c145c0bf29b85b21a1ae2921fa * b23044e7f45daf4d70695fb7bf249432 =
    7883669ef3001d7fabf83784d52eb414
    + +
    + + + + + + + + +
    + +4     IMPORTANT INFORMATION ON ALIGNMENT WHEN MULTIPLYING REGIONS 12


    + +
    +multiply_region by e252d9c145c0bf29b85b21a1ae2921fa
    +R1 (the source): f4f56f08fa92494c5faa57ddcd874149 b4c06a61adbbec2f4b0ffc68e43008cb
    +R2 (the product): b1e34d34b031660676965b868b892043 382f12719ffe3978385f5d97540a13a1
    +UNIX> gf_mult e252d9c145c0bf29b85b21a1ae2921fa f4f56f08fa92494c5faa57ddcd874149 128h
    +b1e34d34b031660676965b868b892043
    +UNIX> gf_div 382f12719ffe3978385f5d97540a13a1 b4c06a61adbbec2f4b0ffc68e43008cb 128h
    +e252d9c145c0bf29b85b21a1ae2921fa
    +UNIX>

    + +
    + + +

    5      Important Information on Alignment when Multiplying Regions

    + + + +In order to make multiplication of regions fast, we often employ 64 and 128 bit instructions. This has ramifications +for pointer alignment, because we want to avoid bus errors, and because on many machines, loading and manipulating +aligned quantities is much faster than unalinged quantities.

    + + +When you perform multiply_region.wxx(gf, source, dest, value, size, add ), there are three requirements: +
      +
    1. + The pointers source and dest must be aligned for w-bit words. For w = 4 and w = 8, there is no restriction; +however for w = 16, the pointers must be multiples of 2, for w = 32, they must be multiples of 4, and for +w ϵ {64, 128}, they must be multiples of 8.

    2. + +
    3. The size must be a multiple of [ w / + +8 .] + With w = 4 and w = 8, w/ +8 = 1 and there is no restriction. The other +sizes must be multiples of w / +8 because you have to be multiplying whole elements of GF(2w ) .

    4. + +
    5. The source and dest pointers must be aligned identically with respect to each other for the implementation +chosen. This is subtle, and we explain it in detail in the next few paragraphs. However, if you'd rather not figure +it out, the following recommendation will always work in GF-Complete:
    6. + +
    + + + +
    +If you want to be safe, make sure that source and dest are both multiples of 16. That is not a +strict requirement, but it will always work!

    +
    + + +If you want to relax the above recommendation, please read further. +

    When performing multiply_region.wxx() , the implementation is typically optimized for a region of bytes whose +size must be a multiple of a variable s ,, and which must be aligned to a multiple of another variable t . For example, +when doing multiply_region.w32() in GF(216 ) with SSE enabled, the implementation is optimized for regions of +32 bytes, which must be aligned on a 16-byte quantity. Thus, s = 32 and t = 16. However, we don't want multiply_ +region.w32() to be too restrictive, so instead of requiring source and dest to be aligned to 16-byte regions, we +require that (source mod 16) equal (dest mod 16). Or, in general, that (source mod t) equal (dest mod t).

    + + +

    +Then, multiply_region.wxx() proceeds in three phases. In the first phase, multiply.wxx() is called on successive +words until (source mod t) equals zero. The second phase then performs the optimized region multiplication on +chunks of s bytes, until the remaining part of the region is less than s bytes. At that point, the third phase calls +multiply.wxx() on the last part of the region.

    + +A detailed example helps to illustrate. Suppose we make the following call in GF(216) with SSE enabled:

    +
    multiply region.w32(gf, 0x10006, 0x20006, a, 274, 0)
    + + + + + + + +
    + +2     FILES IN THE LIBRARY 13


    + + + +



    + +Figure 2: Example of multiplying a region of 274 bytes in GF(216) when (source mod 16) = (dest mod 16) = 6. The +alignment parameters are s = 32 and t = 16. The multiplication is in three phases, which correspond to the initial +unaligned region (10 bytes), the aligned region of s-byte chunks (256 bytes), and the final leftover region (8 bytes). + + +

    First, note that source and dest are aligned on two-byte quantities, which they must be in GF(216). Second, note +that size is a multiple of [ 16/ +8 ] = 2. And last, note that (source mod 16) equals (dest mod 16). We illustrate the three +phases of region multiplication in Figure 2. Because (source mod 16) = 6, there are 10 bytes of unaligned words that +are multiplied with five calls to multiply.w32() in the first phase. The second phase multiplies 256 bytes (eight chunks +of s = 32 bytes) using the SSE instructions. That leaves 8 bytes remaining for the third phase. +

    + +

    +When we describe the defaults and the various implementation options, we specify s and t as "alignment parameters." +

    +

    +One of the advanced region options is using an alternate mapping of words to memory ("ALTMAP"). These interact +in a more subtle manner with alignment. Please see Section 7.9 for details. +

    + +

    6    The Defaults

    + + +GF-Complete implements a wide variety of techniques for multiplication, division and region multiplication. We have +set the defaults with three considerations in mind: +
      +
    1. +Speed: Obviously, we want the implementations to be fast. Therefore, we choose the fastest implementations +that don’t violate the other considerations. The compilation environment is considered. For example, if SSE is +enabled, region multiplication in GF(24 ) employs a single multiplication table. If SSE is not enabled, then a +"double" table is employed that performs table lookup two bytes at a time.

    2. +
    3. +Memory Consumption: We try to keep the memory footprint of GF-Complete low. For example, the fastest +way to perform multiply.w32() in GF(232) is to employ 1.75 MB of multiplication tables (see Section 7.4 +below). We do not include this as a default, however, because we want to keep the default memory consumption +of GF-Complete low. +
    4. + +
+ + + + + + +
+ +6     THE DEFAULTS 14


+ +
    + +3.   Compatibility with "standard" implementations: While there is no de facto standard of Galois Field arithmetic, +most libraries implement the same fields. For that reason, we have not selected composite fields, alternate +polynomials or memory layouts for the defaults, even though these would be faster. Again, see section 7.7 for +more information. + +
+ +

Table 1 shows the default methods used for each power-of-two word size, their alignment parameters s and t, their +memory consumption and their rough performance. The performance tests are on an Intel Core i7-3770 running at +3.40 GHz, and are included solely to give a flavor of performance on a standard microprocessor. Some processors +will be faster with some techniques and others will be slower, so we only put numbers in so that you can ballpark it. +For other values of w between 1 and 31, we use table lookup when w ≤ 8, discrete logarithms when w ≤ 16 and +"Bytwop" for w ≤ 32.

+

+
With SSE +
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + +
w Memory
Usage
multiply()
Implementation
Performance
(Mega Ops / s)
multiply region()
Implementation
s t Performance
(MB/s)
4 <1K Table501Table16 16 11,659
8 136K Table501Split Table (8,4)16 16 11,824
16 896K Log260Split Table (16,4)32 16 7,749
32 <1K Carry-Free48Split Table (32,4)64 16 5,011
64 2K Carry-Free84Split Table (64,4)128 16 2,402
128 64K Carry-Free48Split Table (128,4)16 16 833
+ + +
+
Without SE
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + +
w Memory
Usage
multiply()
Implementation
Performance
(Mega Ops / s)
multiply region()
Implementation
s t Performance
(MB/s)
4 4K Table501Double Table16 16 11,659
8 128K Table501Table1 1 1,397
16 896K Log266Split Table (16,8)32 16 2,135
32 4K Bytwop19Split Table (32,4)4 4 1,149
64 16K Bytwop9Split Table (64,4)8 8 987
128 64K Bytwop1.4Split Table (128,4)16 8 833
+
+
+

+Table 1: The default implementations, memory consumption and rough performance when w is a power of two. The +variables s and t are alignment variables described in Section 5. +

+A few comments on Table 1 are in order. First, with SSE, the performance of multiply() is faster when w = 64 +than when w = 32. That is because the primitive polynomial for w = 32, that has historically been used in Galois +Field implementations, is sub-ideal for using carry-free multiplication (PCLMUL). You can change this polynomial +(see section 7.7) so that the performance matches w = 64.

+

+The region operations for w = 4 and w = 8 without SSE have been selected to have a low memory footprint. There +are better options that consume more memory, or that only work on large memory regions (see section 6.1.5). +

+ +There are times that you may want to stray from the defaults. For example: +
    +
  • +You may want better performance. +
  • + +
+ + + + + + + + + + +
+ +6     THE DEFAULTS 15


+ +
    +
  • You may want a lower memory footprint.
  • +
  • You may want to use a different Galois Field or even a ring.
  • +
  • You only care about multiplying a region by the value two.
  • + +
+ + +

+Our command line tools allow you to deviate from the defaults, and we have two C functions -gf_init_hard() +and create_gf_from_argv() that can be called from application code to override the default methods. There are six +command-line tools that can be used to explore the many techniques implemented in GF-Complete:

+ +

    + +
  • gf_methods is a tool that enumerates most of the possible command-line arguments that can be sent to the other +tools

  • +
  • gf_mult and gf_div are explained above. You may change the multiplication and division technique in these +tools if you desire

  • +
  • gf_unit performs unit tests on a set of techniques to verify correctness

  • +
  • gf_time measures the performance of a particular set of techniques

  • +
  • time_tool.sh makes some quick calls to gf_time so that you may gauge rough performance.

  • +
  • gf_poly tests the irreducibility of polynomials in a Galois Field

  • +
+ + +

To change the default behavior in application code, you need to call gf_init_hard() rather than gf_init_easy(). +Alternatively, you can use create_g_from_argv(), included from gf_method.h, which uses an argv-style array of +strings to specify the options that you want. The procedure in gf_method.c parses the array and makes the proper +gf_init_hard() procedure call. This is the technique used to parse the command line in gf_mult, gf_div, gf_unit et al.

+ + +

6.1.1 Changing the Components of a Galois Field with create gf_from_argv()

+There are five main components to every Galois Field instance: +
    +
  • w
  • +
  • Multiplication technique
  • +
  • Division technique
  • +
  • Region technique(s)
  • +
  • Polynomial
  • +
+ +

The procedures gf_init_hard() and create_gf_from_argv() allow you to specify these parameters when you create +your Galois Field instance. We focus first on create_gf_from_argv(), because that is how the tools allow you to specify +the components. The prototype of create_gf_from_argv() is as follows:


+ +
+int create_gf_from_argv(gf_t *gf, int w, int argc, char **argv, int starting);

+ +You pass it a pointer to a gf_t, which it will initialize. You specify the word size with the parameter w, and then you +pass it an argc/argv pair as in any C or C++ program. You also specify a starting argument, which is where in argv +the specifications begin. If it successfully parses argc and argv, then it creates the gf_t using gf_init_hard() (described +below in section 6.4). It returns one past the last index of argv that it considered when creating the gf_t. If it fails, then +it returns zero, and the gf_t is unmodified. + + + +

For example, gf_mult.c calls create gf_from_argv() by simply passing argc and argv from its main() declaration, +and setting starting to 4.

+ + + + + + + + +
+ +6     THE DEFAULTS 16


+ +

+To choose defaults, argv[starting] should equal "-". Otherwise, you specify the component that you are changing +with "-m" for multiplication technique, "-d" for division technique, "-r" for region technique, and "-p" for the +polynomial. You may change multiple components. You end your specification with a single dash. For example, the +following call multiplies 6 and 5 in GF(24) with polynomial 0x19 using the "SHIFT" technique for multiplication +(we'll explain these parameters later): +



+ +
+UNIX> ./gf_mult 6 5 4 -p 0x19 -m SHIFT -
+7
+UNIX>

+
+ +

If create_gf_from_argv() fails, then you can call the procedure gf_error(), which prints out the reason why create_ +gf_from_argv() failed.

+ + +

6.1.2 Changing the Polynomial

+ +Galois Fields are typically implemented by representing numbers as polynomials with binary coefficients, and then +using the properties of polynomials to define addition and multiplication. You do not need to understand any of that to +use this library. However, if you want to learn more about polynomial representations and how they construct fields, +please refer to The Paper. + +

Multiplication is based on a special polynomial that we will refer to here as the "defining polynomial." This +polynomial has binary coefficients and is of degree w. You may change the polynomial with "-p" and then a number +in hexadecimal (the leading "0x" is optional). It is assumed that the w-th bit of the polynomial is set - you may include +it or omit it. For example, if you wish to set the polynomial for GF(216) to x16 + x5 + x3 + x2 + 1, rather than its +default of x16 + x12 + x3 + x + 1, you may say "-p 0x1002d," "-p 1002d," "-p 0x2d" or "-p 2d." +We discuss changing the polynomial for three reasons in other sections:

+
    +
  • Leveraging carry-free multiplication (section 7.7).
  • +
  • Defining composite fields (section 7.6).
  • +
  • Implementing rings (section 7.8.1).
  • + +
+ +

+Some words about nomenclature with respect to the polynomial. A Galois Field requires the polynomial to be +irreducible .. That means that it cannot be factored. For example, when the coefficients are binary, the polynomial x5+ +x4+x+1 may be factored as (x4+1)(x+1). Therefore it is not irreducible and cannot be used to define a Galois Field. +It may, however, be used to define a ring. Please see section 7.8.1 for a discussion of ring support in GF-Complete.

+

+There is a subset of irreducible polynomials called primitive. These have an important property that one may enumerate +all of the elements of the field by raising 2 to successive posers. All of the default polynomials in GF-Complete +are primitive. However, so long as a polynomial is irreducible, it defines a Galois Field. Please see section 7.7 for a +further discussion of the polynomial.

+ +

+One thing that we want to stress here is that changing the polynomial changes the field, so fields with different +polynomialsmay not be used interchangeably. So long as the polynomial is irreducible, it generates a Galois Field that +is isomorphic to all other Galois Fields; however the multiplication and division of elements will differ. For example, +the polynomials 0x13 (the default) and 0x19 in GF(24) are both irreducible, so both generate valid Galois Fields. +However, their multiplication differs:


+ +
+UNIX> gf_mult 8 2 4 -p 0x13 -
+3
+UNIX> gf_mult 8 2 4 -p 0x19 -
+9
+
+ + + + + + + + + +
+ +6     THE DEFAULTS 17


+ +
+UNIX> gf_div 3 8 4 -p 0x13 -
+2
+UNIX> gf_div 9 8 4 -p 0x19 -
+2
+UNIX>
+ +
+ + +

6.1.3     Changing the Multiplication Technique

+The following list describes the multiplication techinques that may be changed with "-m". We keep the description +here brief. Please refer to The Paper for detailed descriptions of these techniques.

+ + +
  • "TABLE:" Multiplication and division are implemented with tables. The tables consume quite a bit of memory +(2w × 2 w × w/ +8 bytes), so they are most useful when w is small. Please see "SSE," "LAZY," "DOUBLE" and + +"QUAD" under region techniques below for further modifications to "TABLE" to perform multiply_region()

  • + + +
  • "LOG:" This employs discrete (or "Zeph") logarithm tables to implement multiplication and division. The +memory usage is roughly (3 × 2w × w / +8 bytes), so they are most useful when w is small, but they tolerate +larger w than "TABLE." If the polynomial is not primitive (see section 6.1.2), then you cannot use "LOG" as +an implementation. In that case, gf_init_hard() or create_gf_from_argv() will fail

  • + + +
  • "LOG_ZERO:" Discrete logarithm tables which include extra room for zero entries. This more than doubles +the memory consumption to remove an if statement (please see [GMS08] or The Paper for more description). It +doesn’t really make a huge deal of difference in performance

  • + +
  • "LOG_ZERO_EXT:" This expends even more memory to remove another if statement. Again, please see The +Paper for an explanation. As with "LOG_ZERO," the performance difference is negligible

  • + +
  • "SHIFT:" Implementation straight from the definition of Galois Field multiplication, by shifting and XOR-ing, +then reducing the product using the polynomial. This is slooooooooow, so we don’t recommend you use it

  • + + +
  • "CARRY_FREE:" This is identical to "SHIFT," however it leverages the SSE instruction PCLMUL to perform +carry-freemultiplications in single instructions. As such, it is the fastest way to perform multiplication for large +values of w when that instruction is available. Its performance depends on the polynomial used. See The Paper +for details, and see section 7.7 below for the speedups available when w = 16 and w = 32 if you use a different +polynomial than the default one

  • + + +
  • "BYTWO_p:" This implements multiplication by successively multiplying the product by two and selectively +XOR-ing the multiplicand. See The Paper for more detail. It can leverage Anvin’s optimization that multiplies +64 and 128 bits of numbers in GF(2w) by two with just a few instructions. The SSE version requires SSE2

  • + + +
  • "BYTWO_b:" This implements multiplication by successively multiplying the multiplicand by two and selectively +XOR-ing it into the product. It can also leverage Anvin's optimization, and it has the feature that when +you're multiplying a region by a very small constant (like 2), it can terminate the multiplication early. As such, +if you are multiplying regions of bytes by two (as in the Linux RAID-6 Reed-Solomon code [Anv09]), this is +the fastest of the techniques, regardless of the value of w. The SSE version requires SSE2

  • + + +
  • "SPLIT:" Split multiplication tables (like the LR tables in [GMS08], or the SIMD tables for w ≤ 8 in [LHy08, +Anv09, PGM13b]). This argument must be followed by two more arguments, wa and wb, which are the index +sizes of the sub-tables. This implementation reduces the size of the table from "TABLE," but requires multiple +

  • + + + + + + +
    + +6     THE DEFAULTS 18


    +
      +table lookups. For example, the following multiplies 100 and 200 in GF(28) using two 4K tables, as opposed +to one 64K table when you use "TABLE:"

      +
      +UNIX> ./gf_mult 100 200 8 -m SPLIT 8 4 -
      +79
      +UNIX>

      +
      + +See section 7.4 for additional information on the arguments to "SPLIT." The SSE version typically requires +SSSE3.

      + + +
    • "GROUP:" This implements the "left-to-right comb" technique [LBOX12]. I'm afraid we don't like that name, +so we call it "GROUP," because it performs table lookup on groups of bits for shifting (left) and reducing (right). +It takes two additional arguments - gs, which is the number of bits you use while shifting (left) and gr, which +is the number of bits you use while reducing (right). Increasing these arguments can you higher computational +speed, but requires more memory. SSE version exists only for w = 128 and it requires SSE4. For more +description on the arguments gs and gr, see section 7.5. For a full description of "GROUP" algorithm, please +see The Paper. +

    • + +
    • "COMPOSITE:" This allows you to perform operations on a composite Galois Field, GF((2l)k) as described +in [GMS08], [LBOX12] and The Paper. The field size w is equal to lk. It takes one argument, which is k, and +then a specification of the base field. Currently, the only value of k that is supported is two. However, that may +change in a future revision of the library.

    • + + +In order to specify the base field, put appropriate flags after specifying k. The single dash ends the base field, +and after that, you may continue making specifications for the composite field. This process can be continued +for multiple layers of "COMPOSITE." As an example, the following multiplies 1000000 and 2000000 +in GF((216)2), where the base field uses BYTWO_p for multiplication:

      +
      ./gf_mult 1000000 2000000 32 -m COMPOSITE 2 -m BYTWO_p - -

      + +In the above example, the red text applies to the base field, and the black text applies to the composite field. +Composite fields have two defining polynomials - one for the composite field, and one for the base field. Thus, if +you want to change polynomials, you should change both. The polynomial for the composite field must be of the +form x2+sx+1, where s is an element of GF(2k). To change it, you specify s (in hexadecimal)with "-p." In the +example below, we multiply 20000 and 30000 in GF((28)2) , setting s to three, and using x8+x4+x3+x2+1 +as the polynomial for the base field:

      + +
      ./gf_mult 20000 30000 16 -m COMPOSITE 2 -p 0x11d - -p 0x3 -


      + +If you use composite fields, you should consider using "ALTMAP" as well. The reason is that the region +operations will go much faster. Please see section 7.6.

      +As with changing the polynomial, when you use a composite field, GF((2l)k), you are using a different field +than the "standard" field for GF((2l)k). All Galois Fields are isomorphic to each other, so they all have the +desired properties; however, the fields themselves change when you use composite fields.

      +
    +

    +With the exception of "COMPOSITE", only one multiplication technique can be provided for a given Galois +Field instance. Composite fields may use composite fields as their base fields, in which case the specification will be +recursive.

    + + + + + + + + +
    + +6     THE DEFAULTS 19


    + +

    6.1.4       Changing the Division Technique

    + +There are two techniques for division that may be set with "-d". If "-d" is not specified, then appropriate defaults +are employed. For example, when the multiplication technique is "TABLE," a table is created for division as well as +multiplication. When "LOG" is specified, the logarithm tables are used for division. With "COMPOSITE," a special +variant of Euclid's algorithm is employed that performs division using multiplication and division in the base field. +Otherwise, Euclid's algorithm is used. Please see The Paper for a description of Euclid's algorithm applied to Galois +Fields. + +

    If you use "-d", you must also specify the multiplication technique with "-m."

    +

    To force Euclid's algorithm instead of the defaults, you may specify it with "-d EUCLID." If instead, you would +rather convert elements of a Galois Field to a binary matrix and find an element's inverse by inverting the matrix, +then specify "-d MATRIX." In all of our tests, "MATRIX" is slower than "EUCLID." "MATRIX" is also not defined +for w > 32. +

    + + +

    6.1.5     Changing the Region Technique

    +The following are the region multiplication options ("-r"): +
      +
    • +"SSE:" Use SSE instructions. Initialization will fail if the instructions aren't supported. Table 2 details the +multiplication techniques which can leverage SSE instructions and which versions of SSE are required.

    • + +
      +
      + + + + + + + + + + + + + + + + + + + + +
      Multiplication
      Technique
      multiply() multiply_region() SSE Version Comments
      "TABLE"- YesSSSE3Only for GF(24).
      "SPLIT"-YesSSSE3Only when the second argument equals 4.
      "SPLIT"- YesSSE4When w = 64 and not using "ALTMAP".
      "BYTWO_p"- YesSSE2
      "BYTWO_p"- YesSSE2


      +Table 2: Multiplication techniques which can leverage SSE instructions when they are available. +


      + + + + + + + + + + + + +
    • "NOSSE:" Force non-SSE version

    • + +
    • "DOUBLE:" Use a table that is indexed on two words rather than one. This applies only to w = 4, where +the table is indexed on bytes rather than 4-bit quantities, and to w = 8, where the table is indexed on shorts +rather than bytes. In each case, the table lookup performs two multiplications at a time, which makes region +multiplication faster. It doubles the size of the lookup table.

    • + +
    • "QUAD:" Use a table that is indexed on four words rather than two or one. This only applies to w = 4, where +the table is indexed on shorts. The "Quad" table may be lazily created or created ahead of time (the default). If +the latter, then it consumes 24 × 216 × 2 = 2 MB of memory.

    • + +
    • "LAZY:" Typically it's clear whether tables are constructed upon initialization or lazily when a region operation +is performed. There are two times where it is ambiguous: "QUAD" when w = 4 and "DOUBLE" when w = 8. +If you don't specify anything, these tables are created upon initialization, consuming a lot of memory. If you +specify "LAZY," then the necessary row of the table is created lazily when you call "multiply_region(). +
    • + +
    + + + + + + + + + + + +
    + +6     THE DEFAULTS 20


    +
      + +
    • "ALTMAP:" Use an alternate mapping, where words are split across different subregions of memory. There +are two places where this matters. The first is when implementing "SPLIT w 4" using SSE when w > 8. In +these cases, each byte of the word is stored in a different 128-bit vector, which allows the implementation to +better leverage 16-byte table lookups. See section 7.4 for examples, and The Paper or [PGM13b] for detailed +explanations.

    • + +The second place where it matters is when using "COMPOSITE." In this case, it is advantageous to split each +memory region into two chunks, and to store half of each word in a different chunk. This allows us to call +region_multiply() recursively on the base field, which is much faster than the alternative. See Section 7.6 for +examples, and The Paper for an explanation.

      + +It is important to note that with "ALTMAP," the words are not "converted" from a standard mapping to an +alternate mapping and back again. They are assumed to always be in the alternate mapping. This typically +doesn't matter, so long as you always use the same "ALTMAP" calls. Please see section 7.9 for further details +on "ALTMAP," especially with respect to alignment.

      + +
    • "CAUCHY:" Break memory into w subregions and perform only XOR's as in Cauchy Reed-Solomon coding +[BKK+95] (also described in The Paper). This works for any value of w ≤ 32, even those that are not +powers of two. If SSE2 is available, then XOR's work 128 bits at a time. For "CAUCHY" to work correctly, +size must be a multiple of w .
    + + + +

    It is possible to combine region multiplication options. This is fully supported as long as gf_methods has the combination +listed. If multiple region options are required, they should be specified independently (as flags for gf_init_hard() +and independent options for command-line tools and create_gf_from_argv()).

    + + +

    6.2    Determining Supported Techniques with gf_methods

    + + +The program gf_methods prints a list of supported methods on standard output. It is called as follows:

    +
    +
    ./gf_methods w -BADC -LUMDRB

    + +The first argument is w , which may be any legal value of w . The second argument has the following flags:

    +
      + +
    • "B:" This only prints out "basic" methods that are useful for the given value of w . It omits "SHIFT" and other +methods that are never really going to be useful.

    • + +
    • "A:" In constrast, this specifies to print "all" methods.

    • + +
    • "D:" This includes the "EUCLID" and "MATRIX" methods for division. By default, they are not included.

    • + +
    • "C:" This includes the "CAUCHY" methods for region multiplication. By default, it is not included.

    • +
    +

    +You may specify multiple of these as the second argument. If you include both "B" and "A," then it uses the last +one specified.

    +

    +The last argument determines the output format of gf_methods. If it is "L," then it simply lists methods. If it +is "U," then the output contains gf_unit commands for each of the methods. For the others, the output contains +gf_time_tool.sh commands for M ultiplication,Division,Region multiplications with multiple buffer sizes, and the +Best region multiplication.

    +

    +gf_methods enumerates combinations of flags, and calls create_gf_from_argv() to see if the combinations are +supported. Although it enumerates a large number of combinations, it doesn't enumerate all possible parameters for +"SPLIT," "GROUP" or "COMPOSITE."

    + +

    Some examples of calling gf_methods are shown below in section 6.3.2.

    + + + + + + + +
    + +6     THE DEFAULTS 21


    + + +

    6.3 Testing with gf_unit , gf_time , and time_tool.sh

    + + + +gf_unit and gf_time may be used to verify that a combination of arguments works correctly and efficiently on your +platform. If you plan to stray from the defaults, it is probably best to run both tools to ensure there are no issues with +your environment. gf_unit will run a set of unit tests based on the arguments provided to the tool, and gf_time will +time Galois Field methods based on the provided arguments.
    +The usage of gf_ unit is:

    +
    +gf_unit w tests seed method

    +The usage of gf_ time is:

    +
    +gf_time w tests seed buffer-size iterations method

    +
    + + + +The seed is an integer- negative one uses the current time. The tests are specified by a listing of characters. The +following tests are supported (All are supported by gf_time. Only ', 'S' and 'R' are supported by gf_unit):

    + +
      +
    • 'M': Single multiplications

    • +
    • 'D': Single divisions

    • +
    • 'I': Single inverses

    • +
    • 'G': Region multiplication of a buffer by a random constant

    • +
    • '0': Region multiplication of a buffer by zero (does nothing andbzero())

    • +
    • '1': Region multiplication of a buffer by one (does memcpy() and XOR)

    • +
    • '2': Region multiplication of a buffer by two – sometimes this is faster than general multiplication

    • +
    • 'S': All three single tests

    • +
    • 'R': All four region tests

    • +
    • 'A': All seven tests

    • +
    + + + + + +

    Here are some examples of calling gf_unit and gf_time to verify that "-m SPLIT 32 4 -r ALTMAP -" works +in GF(232), and to get a feel for its performance. First, we go to the test directory and call gf_unit:



    + + +
    +UNIX> cd test
    +UNIX> ./gf_unit 32 A -1 -m SPLIT 32 4 -r ALTMAP -
    +Args: 32 A -1 -m SPLIT 32 4 -r ALTMAP - / size (bytes): 684
    +UNIX>

    +
    + +gf_unit reports on the arguments and how may bytes the gf_t consumes. If it discovers any problems or inconsistencies +with multiplication, division or region multiplication, it will report them. Here, there are no problems. +Next, we move to the tools directory and run performance tests on a 10K buffer, with 10,000 iterations of each test:

    + + +UNIX> cd ../tools
    +UNIX> ./gf_time 32 A -1 10240 10000 -m SPLIT 32 4 -r ALTMAP -
    +Seed: 1388435794
    +
    + + + + + + + + + + + + +
    Multiply: 4.090548 s Mops: 24.414 5.968 Mega-ops/s
    Divide: 37.794962 s Mops: 24.414 0.646 Mega-ops/s
    Inverse: 33.709875 s Mops: 24.414 0.724 Mega-ops/s
    Region-Random: XOR: 0 0.035210 s MB: 97.656 2773.527 MB/s
    Region-Random: XOR: 1 0.036081 s MB: 97.656 2706.578 MB/s
    Region-By-Zero:XOR: 0 0.003199 s MB: 97.656 30523.884 MB/s
    Region-By-Zero: XOR: 1 0.000626 s MB: 97.656 156038.095 MB/s
    +
    + + + + + + + + + + +
    + +6     THE DEFAULTS 22


    + +
    + + + + + + + +
    Region-By-One: XOR: 0 0.003810 s MB: 97.656 25628.832 MB/s
    Region-By-One: XOR: 1 0.008363 s MB: 97.656 11677.500 MB/s
    Region-By-Two: XOR: 0 0.032942 s MB: 97.656 2964.486 MB/s
    Region-By-Two: XOR: 1 0.033488 s MB: 97.656 2916.153 MB/s
    +
    +UNIX>

    + +

    The first column of output displays the name of the test performed. Region tests will test with and without the XOR +flag being set (see Section 4.3 for an example). The second column displays the total time the test took to complete +measured in seconds (s). The third column displays the size of the test measured in millions of operations (Mops) for +single tests and in Megabytes (MB) for the region tests. The final column displays the speed of the tests calculated +from the second and third columns, and is where you should look to get an idea of a method's performance.

    +

    +If the output of gf_unit and gf_time are to your satisfaction, you can incorporate the method into application code +using create gf_from_argv() or gf_init hard().

    +

    +The performance of "Region-By-Zero" and "Region-By-One" will not change from test to test, as all methods make +the same calls for these. "Region-By-Zero" with "XOR: 1" does nothing except set up the tests. Therefore, you may +use it as a control.

    + +

    6.3.1       time_tool.sh

    + +Finally, the shell script time_tool.sh makes a bunch of calls to gf_time to give a rough estimate of performance. It is +called as follows:

    +usage sh time_tool.sh M|D|R|B w method

    + + +

    The values for the first argument are MDRB, for Multiplication, Division,Region multiplications with multiple +buffer sizes, and the Best region multiplication. For the example above, let's call time_tool.sh to get a rough idea of +performance:



    + +
    +UNIX> sh time_tool.sh M 32 -m SPLIT 32 4 -r ALTMAP -
    +M speed (MB/s): 6.03 W-Method: 32 -m SPLIT 32 4 -r ALTMAP -
    +UNIX> sh time_tool.sh D 32 -m SPLIT 32 4 -r ALTMAP -
    +D speed (MB/s): 0.65 W-Method: 32 -m SPLIT 32 4 -r ALTMAP -
    +UNIX> sh time_tool.sh R 32 -m SPLIT 32 4 -r ALTMAP -
    + + + + + + + + + + + + + +
    Region Buffer-Size: 16K (MB/s): 3082.91 W-Method: 32 -m SPLIT 32 4 -r ALTMAP -
    Region Buffer-Size: 32K (MB/s): 3529.07 W-Method: 32 -m SPLIT 32 4 -r ALTMAP -
    Region Buffer-Size: 64K (MB/s): 3749.94 W-Method: 32 -m SPLIT 32 4 -r ALTMAP -
    Region Buffer-Size: 128K (MB/s): 3861.27 W-Method: 32 -m SPLIT 32 4 -r ALTMAP -
    Region Buffer-Size: 512K (MB/s): 3820.82 W-Method: 32 -m SPLIT 32 4 -r ALTMAP -
    Region Buffer-Size: 1M (MB/s): 3737.41 W-Method: 32 -m SPLIT 32 4 -r ALTMAP -
    Region Buffer-Size: 2M (MB/s): 3002.90 W-Method: 32 -m SPLIT 32 4 -r ALTMAP -
    Region Buffer-Size: 4M (MB/s): 2760.77 W-Method: 32 -m SPLIT 32 4 -r ALTMAP -
    Region Best (MB/s): 3861.27 W-Method: 32 -m SPLIT 32 4 -r ALTMAP -
    + +UNIX> sh time_tool.sh B 32 -m SPLIT 32 4 -r ALTMAP -
    +Region Best (MB/s): 3929.09 W-Method: 32 -m SPLIT 32 4 -r ALTMAP -
    +UNIX>

    +
    +

    +We say that time_tool.sh is "rough" because it tries to limit each test to 5 ms or less. Thus, the time granularity +is fine, which means that the numbers may not be as precise as they could be were the time granularity to be course. +When in doubt, you should make your own calls to gf_time with a lot of iterations, so that startup costs and roundoff +error may be minimized.

    + + + + + + + + +
    + +6     THE DEFAULTS 23


    + +

    6.3.2       An example of gf_methods and time_tool.sh



    +Let's give an example of how some of these components fit together. Suppose we want to explore the basic techniques +in GF(232). First, let's take a look at what gf_methods suggests as "basic" methods:

    +
    +UNIX> gf_methods 32 -B -L
    +w=32: -
    +w=32: -m GROUP 4 8 -
    +w=32: -m SPLIT 32 4 -
    +w=32: -m SPLIT 32 4 -r ALTMAP -
    +w=32: -m SPLIT 32 8 -
    +w=32: -m SPLIT 8 8 -
    +w=32: -m COMPOSITE 2 - -
    +w=32: -m COMPOSITE 2 - -r ALTMAP -
    +UNIX>

    +
    + + +

    + +You'll note, this is on my old Macbook Pro, which doesn't support (PCLMUL), so "CARRY_FREE" is not included +as an option. Now, let's run the unit tester on these to make sure they work, and to see their memory consumption:



    + +
    +UNIX> gf_methods 32 -B -U
    +../test/gf_unit 32 A -1 -
    +../test/gf_unit 32 A -1 -m GROUP 4 8 -
    +../test/gf_unit 32 A -1 -m SPLIT 32 4 -
    +../test/gf_unit 32 A -1 -m SPLIT 32 4 -r ALTMAP -
    +../test/gf_unit 32 A -1 -m SPLIT 32 8 -
    +../test/gf_unit 32 A -1 -m SPLIT 8 8 -
    +../test/gf_unit 32 A -1 -m COMPOSITE 2 - -
    +../test/gf_unit 32 A -1 -m COMPOSITE 2 - -r ALTMAP -
    +UNIX> gf_methods 32 -B -U | sh
    +Args: 32 A -1 - / size (bytes): 684
    +Args: 32 A -1 -m GROUP 4 8 - / size (bytes): 1296
    +Args: 32 A -1 -m SPLIT 32 4 - / size (bytes): 684
    +Args: 32 A -1 -m SPLIT 32 4 -r ALTMAP - / size (bytes): 684
    +Args: 32 A -1 -m SPLIT 32 8 - / size (bytes): 4268
    +Args: 32 A -1 -m SPLIT 8 8 - / size (bytes): 1839276
    +Args: 32 A -1 -m COMPOSITE 2 - - / size (bytes): 524648
    +Args: 32 A -1 -m COMPOSITE 2 - -r ALTMAP - / size (bytes): 524648
    +UNIX>

    +
    +

    +As anticipated, "SPLIT 8 8" consumes quite a bit of memory! Now, let's see how well they perform with both +single multiplications and region multiplications:



    +
    +UNIX> gf_methods 32 -B -M
    +sh time_tool.sh M 32 -
    +sh time_tool.sh M 32 -m GROUP 4 8 -
    +sh time_tool.sh M 32 -m SPLIT 32 4 -
    +sh time_tool.sh M 32 -m SPLIT 32 4 -r ALTMAP -
    +sh time_tool.sh M 32 -m SPLIT 32 8 -
    +sh time_tool.sh M 32 -m SPLIT 8 8 -
    + +
    + + + + + + + + +
    + +6     THE DEFAULTS 24


    + +
    +sh time_tool.sh M 32 -m COMPOSITE 2 -
    +sh time_tool.sh M 32 -m COMPOSITE 2 - -r ALTMAP
    +UNIX> gf_methods 32 -B -M | sh +M speed (MB/s): 5.90 W-Method: 32
    +M speed (MB/s): 14.09 W-Method: 32 -m GROUP 4 8
    +M speed (MB/s): 5.60 W-Method: 32 -m SPLIT 32 4
    +M speed (MB/s): 5.19 W-Method: 32 -m SPLIT 32 4 -r ALTMAP
    +M speed (MB/s): 5.98 W-Method: 32 -m SPLIT 32 8
    +M speed (MB/s): 22.10 W-Method: 32 -m SPLIT 8 8
    +M speed (MB/s): 34.98 W-Method: 32 -m COMPOSITE 2 -
    +M speed (MB/s): 34.16 W-Method: 32 -m COMPOSITE 2 - -r ALTMAP
    +UNIX> gf_methods 32 -B -B | sh +Region Best (MB/s): 2746.76 W-Method: 32
    +Region Best (MB/s): 177.06 W-Method: 32 -m GROUP 4 8
    +Region Best (MB/s): 2818.75 W-Method: 32 -m SPLIT 32 4
    +Region Best (MB/s): 3818.21 W-Method: 32 -m SPLIT 32 4 -r ALTMAP
    +Region Best (MB/s): 728.68 W-Method: 32 -m SPLIT 32 8
    +Region Best (MB/s): 730.97 W-Method: 32 -m SPLIT 8 8
    +Region Best (MB/s): 190.20 W-Method: 32 -m COMPOSITE 2 -
    +Region Best (MB/s): 1837.99 W-Method: 32 -m COMPOSITE 2 - -r ALTMAP
    +UNIX> +
    +

    +The default is quite a bit slower than the best performing methods for both single and region multiplication. So +why are the defaults the way that they are? As detailed at the beginning of this chapter, we strive for lower memory +consumption, so we don't use "SPLIT 8 8," which consumes 1.75MB.We don't implement alternate fields by default, +which is why we don't use "COMPOSITE." Finally, we don't implement alternate mappings of memory by default, +which is why we don't use "-m SPLIT 32 4 -r ALTMAP -."

    + +

    Of course, you may change these defaults if you please.

    +

    +Test question: Given the numbers above, it would appear that "COMPOSITE" yields the fastest performance of +single multiplication, while "SPLIT 32 4" yields the fastest performance of region multiplication. Should I use two +gf_t's in my application – one for single multiplication that uses "COMPOSITE," and one for region multiplication +that uses "SPLIT 32 4?"

    +

    +The answer to this is "no." Why? Because composite fields are different from the "standard" fields, and if you mix +these two gf_t's, then you are using different fields for single multiplication and region multiplication. Please read +section 7.2 for a little more information on this.

    + +

    6.4      Calling gf_init_hard()

    + +We recommend that you use create_gf_from_argv() instead of gf_init_hard(). However, there are extra things that +you can do with gf_init_hard(). Here's the prototype:

    +
    +int gf_init_hard(gf_t *gf
    +
    +int w
    +int mult_type
    +int region_type
    +int divide_type
    +uint64_t prim_poly
    +int arg1
    +int arg2
    +
    +
    + + + + + + + + +
    + +6     THE DEFAULTS 25


    +
    +
    +GFP base_gf,
    +void *scratch_memory);


    + + +The arguments mult type, region type and divide type allow for the same specifications as above, except the +types are integer constants defined in gf_complete.h:

    +typedef enum {GF_MULT_DEFAULT,
    +
    +GF_MULT_SHIFT
    +GF_MULT_CARRY_FREE
    +GF_MULT_GROUP
    +GF_MULT_BYTWO_p
    +GF_MULT_BYTWO_b
    +GF_MULT_TABLE
    +GF_MULT_LOG_TABLE
    +GF_MULT_LOG_ZERO
    +GF_MULT_LOG_ZERO_EXT
    +GF_MULT_SPLIT_TABLE
    +GF_MULT_COMPOSITE } gf_mult_type_t;

    + +
    + +#define GF_REGION_DEFAULT (0x0)
    +#define GF_REGION_DOUBLE_TABLE (0x1)
    +#define GF_REGION_QUAD_TABLE (0x2)
    +#define GF_REGION_LAZY (0x4)
    +#define GF_REGION_SSE (0x8)
    +#define GF_REGION_NOSSE (0x10)
    +#define GF_REGION_ALTMAP (0x20)
    +#define GF_REGION_CAUCHY (0x40)

    +typedef enum { GF_DIVIDE_DEFAULT
    +
    GF_DIVIDE_MATRIX
    +GF_DIVIDE_EUCLID } gf_division_type_t;

    +
    +
    +

    +You can mix the region types with bitwise or. The arguments to GF_MULT_GROUP,GF_MULT_SPLIT_TABLE +and GF_MULT_COMPOSITE are specified in arg1 and arg2. GF_MULT_COMPOSITE also takes a base field +in base_gf. The base field is itself a gf_t, which should have been created previously with create_gf_fro_argv(), +gf_init_easy() or gf_init_hard(). Note that this base_gf has its own base_gf member and can be a composite field +itself.

    +

    +You can specify an alternate polynomial in prim_poly. For w ≤ 32, the leftmost one (the one in bit position w) is +optional. If you omit it, it will be added for you. For w = 64, there's no room for that one, so you have to leave it off. +For w = 128, your polynomial can only use the bottom-most 64 bits. Fortunately, the standard polynomial only uses +those bits. If you set prim_poly to zero, the library selects the "standard" polynomial. +

    +

    +Finally, scratch_memory is there in case you don't want gf_init_hard() to call malloc(). Youmay call gf_scratch_size() +to find out how much extra memory each technique uses, and then you may pass it a pointer for it to use in scratc_memory. +If you set scratch memory to NULL, then the extra memory is allocated for you with malloc(). If you use gf_init_easy() +or create_gf_from_argv(), or you use gf_init_hard() and set scratch_memory to NULL, then you should call gf_free() +to free memory. If you use gf_init_hard() and use your own scratch_memory you can still call gf_free(), and it will +not do anything.

    +

    +Both gf_init_hard() and gf_scratch_size() return zero if the arguments don't specify a valid gf_t. When that happens, +you can call gf_error() to print why the call failed.

    + + + + + + + + +
    + + +6     FURTHER INFORMATION ON OPTIONS AND ALGORITHMS 26


    + + +

    We'll give you one example of calling gf_ init_hard(). Suppose you want to make a gf_ init_hard() call to be +equivalent to "-m SPLIT 16 4 -r SSE -r ALTMAP -" and you want to allocate the scratch space yourself. Then you'd +do the following:



    + +
    +gf_t gf;
    +void *scratch;
    +int size;
    +size = gf_scratch_size(16, GF_MULT_SPLIT_TABLE,
    +GF_REGION_SSE | GF_REGION_ALTMAP,
    +GF_DIVIDE_DEFAULT,
    +16, 4);
    +if (size == 0) { gf_error(); exit(1); } /* It failed. That shouldn’t happen */
    +scratch = (void *) malloc(size);
    +if (scratch == NULL) { perror("malloc"); exit(1); }
    +if (!gf_init_hard(&gf, 16, GF_MULT_SPLIT_TABLE,
    +GF_REGION_SSE | GF_REGION_ALTMAP,
    +GF_DIVIDE_DEFAULT,
    +0, 16, 4, NULL, scratch)) {
    +gf_error();
    +exit(1);
    +}
    + +
    + + +

    6.5     gf_size()

    + +You can call gf_size(gf_t *gf) to learn the memory consumption of the gf_t. It returns all memory consumed by the +gf_t, including the gf_t itself, any scratch memory required by the gf_ t, and the memory consumed by the sub-field +if the field is "COMPOSITE." If you provided your own memory to gf_init_hard(), it does not report the size of +this memory, but what the size should be, as determined by gf_scratch size(). gf_ unit() prints out the return value of +gf_size() on the given field. + + +

    7   Further Information on Options and Algorithms

    +

    +7.1   Inlining Single Multiplication and Division for Speed

    + +Obviously, procedure calls are more expensive than single instructions, and the mechanics of multiplication in "TABLE" +and "LOG" are pretty simple. For that reason, we support inlining for "TABLE" when w = 4 and w = 8, and +for "LOG" when w = 16. We elaborate below. +

    +When w = 4, you may inline multiplication and division as follows. The following procedures return pointers to +the multiplication and division tables respectively:



    + +
    +uint8_t *gf_w4_get_mult_table(gf_t * gf);
    +uint8_t *gf_w4_get_div_table(gf_t * gf);

    +
    +

    The macro Gf_W4_INLINE_MULTDIV (table, a, b) then multiplies or divides a by b using the given table. This +of course only works if the multiplication technique is "TABLE," which is the default for w = 4. If the multiplication +technique is not "TABLE," then gf_w4_get_mult_table() will return NULL.

    + + + + + + + + +
    + + +6     FURTHER INFORMATION ON OPTIONS AND ALGORITHMS 27


    + + + + +

    When w = 8, the procedures gf_w8_et_mult_table() and gf_ w8_get_div_table(), and the macro

    + +GF_W8_INLINE_MULTDIV (table, a, b) work identically to the w = 4 case. + +

    When w = 16, the following procedures return pointers to the logarithm table, and the two inverse logarithm tables +respectively:


    + +
    +uint16_t *gf_w16_get_log_table(gf_t * gf);
    +uint16_t *gf_w16_get_mult_alog_table(gf_t * gf);
    +uint16_t *gf_w16_get_div_alog_table(gf_t * gf);
    + +
    +
    +

    +The first inverse logarithm table works for multiplication, and the second works for division. They actually point +to the same table, but to different places in the table. You may then use the macro GF_W16_INLINE_MULT(log, +alog, a, b ) to multiply a and b, and the macro GF_W16_INLINE_DIV (log, alog, a, b ) to divide a and b. Make +sure you use the alog table returned by gf_w16_get_mult_alog_table() for multiplication and the one returned by +gf_w16_get_div_alog_table() for division. Here are some timings:



    + + +UNIX> gf_time 4 M 0 10240 10240 -
    +Seed: 0
    +Multiply: 0.228860 s Mops: 100.000 436.949 Mega-ops/s
    +UNIX> gf_inline_time 4 0 10240 10240
    +Seed: 0
    +Inline mult: 0.096859 s Mops: 100.000 1032.424 Mega-ops/s
    +UNIX> gf_time 8 M 0 10240 10240 -
    +Seed: 0
    +Multiply: 0.228931 s Mops: 100.000 436.812 Mega-ops/s
    +UNIX> gf_inline_time 8 0 10240 10240
    +Seed: 0
    +Inline mult: 0.114300 s Mops: 100.000 874.889 Mega-ops/s
    +UNIX> gf_time 16 M 0 10240 10240 -
    +Seed: 0
    +Multiply: 0.193626 s Mops: 50.000 258.229 Mega-ops/s
    +UNIX> gf_inline_time 16 0 10240 10240
    +Seed: 0
    +Inline mult: 0.310229 s Mops: 100.000 322.342 Mega-ops/s
    +UNIX>

    + +

    +7.2     Using different techniques for single and region multiplication

    + + +You may want to "mix and match" the techniques. For example, suppose you'd like to use "-m SPLIT 8 8" for +multiply() in GF(232), because it's fast, and you don't mind consuming all of that space for tables. However, for +multiply_region(), you'd like to use "-m SPLIT 32 4 -r ALTMAP," because that's the fastest way to implement +multiply_region(). Unfortunately, There is no way to create a gf_t that does this combination. In this case, you should +simply create two gf_t's, and use one for multiply() and the other for multiply_region(). All of the implementations +may be used interchangably with the following exceptions: + +
      +
    • +"COMPOSITE" implements a different Galois Field.

    • + +
    • If you change a field's polynomial, then the resulting Galois Field will be different.
    • + +
    + + + + + + + + +
    + + +6     FURTHER INFORMATION ON OPTIONS AND ALGORITHMS 28


    + +
      +
    • + +If you are using "ALTMAP" to multiply regions, then the contents of the resulting regions of memory will +depend on the multiplication technique, the size of the region and its alignment. Please see section 7.9 for a +detailed explanation of this.
    • + +
    • If you are using "CAUCHY" to multiply regions, then like "ALTMAP," the contents of the result regions of +memory the multiplication technique and the size of the region. You don't have to worry about alignment.
    • + +

      7.3     General w

      +The library supports Galois Field arithmetic with 2 < w ≤ 32. Values of w which are not whole number powers of +2 are handled by the functions in gf_wgen.c . For these values of w , the available multiplication types are "SHIFT," +"BYTw O p," "BYTw O b," "GROUP," "TABLE" and "LOG." "LOG" is only valid for w < 28 and "TABLE" + +is only valid for w < 15. The defaults for these values of w are "TABLE" for w < 8, "LOG" for w < 16, and +"BYTw O p" for w < 32.

      + +

      7.4 Arguments to "SPLIT"

      + +The "SPLIT" technique is based on the distributive property of multiplication and addition:

      +
      +a * (b + c) = (a * b) + (a * c).
      +
      +This property allow s us to, for example, split an eight bit w ord into tw o four-bit components and calculate the product +by performing tw o table lookups in 16-element tables on each of the compoents, and adding the result. There is much +more information on "SPLIT" in The Paper. Here w e describe the version of "SPLIT" implemented in GF-Complete. + +

      +"SPLIT" takes tw o arguments, w hich are the number of bits in each component of a, w hich w e call w a, and the +number of bits in each component of b, w hich w e call w b. If the tw o differ, it does not matter w hich is bigger - the +library recognizes this and performs the correct implementation. The legal values of w a and w b fall into five categories: +


      + + +
        +
      1. + w a is equal to w and w b is equal to four. In this case, b is broken up into w /4 +four-bit w ords w hich are used +in 16-element lookup tables. The tables are created on demand in multiply_region() and the SSSE3 instruction + +mm_shuffle_epi8() is leveraged to perform 16 lookups in parallel. Thus, these are very fast implementations. +w hen w ≥ 16, you should combine this w ith "ALTMAP" to get the best performance (see The Paper +or [PGM13b] for explanation). If you do this please see section 7.9 for information about "ALTMAP" and +alignment.

        + + +If you don't use "ALTMAP," the implementations for w ∈ {16, 32, 64} convert the standard representation into +"ALTMAP," perform the multiplication w ith "ALTMAP" and then convert back to the standard representation. +The performance difference using "ALTMAP" can be significant:


        + +
        +
        +
        + + + + + + + + + + + + + + + + + + + + + + + + + + + +
        gf_time 16 G 0 1048576 100 -m SPLIT 16 4 - Speed = 8,389 MB/s
        gf_time 16 G 0 1048576 100 -m SPLIT 16 4 -r ALTMAP - Speed = 8,389 MB/s
        gf_time 32 G 0 1048576 100 -m SPLIT 32 4 - Speed = 5,304 MB/s
        gf_time 32 G 0 1048576 100 -m SPLIT 32 4 -r ALTMAP - Speed = 7,146 MB/s
        gf_time 64 G 0 1048576 100 -m SPLIT 64 4 - Speed = 2,595 MB/s
        gf_time 64 G 0 1048576 100 -m SPLIT 64 4 -r ALTMAP - Speed = 3,436 MB/s
        +
        + + + + + + + + + +
        + + +6     FURTHER INFORMATION ON OPTIONS AND ALGORITHMS 29


        + +
          + + +
        1. 2.   wa is equal to w and wb is equal to eight. Now, b is broken into bytes, each of these is used in its own 256-element +lookup table. This is typically the best way to perform multiply_region() without SSE.
        2. +Because this is a region optimization, when you specify these options, you get a default multiply() see +Table 1 for a listing of the defaults. See section 7.2 for using a different multiply() than the defaults.

          + + +
        3. +3.   wa is equal to w and w b is equal to 16. This is only valid for w = 32 and w = 64. Now , b is broken into shorts, +each of these is used in its own 64K-element lookup table. This is typically slower than when w b equals 8, and +requires more amortization (larger buffer sizes) to be effective.

        4. + + +
        5. 4.   w a and w b are both equal to eight. Now both a and b are broken into bytes, +and the products of the various bytes +are looked up in multiple 256 × 256 tables. In GF(216), there are three of these tables. In GF(232), there are +seven, and in GF(264) there are fifteen. Thus, this implementation can be a space hog. How ever, for w = 32, +this is the fastest way to perform multiply() on some machines. +when this option is employed, multiply_region() is implemented in an identical fashion to when w a = w +and w b = 8.

        6. + +
        7. 5.  wa = 32 and wb = 2. (w = 32 only). I was playing with a different way to use mm_shuffle_epi8(). It works, +but it's slower than when wb = 4. +
        8. + +
    + + + +

    7.5    Arguments to "GROUP"

    + +The "GROUP" multiplication option takes tw o arguments, gs and gr. It implements multiplication in the same manner +as "SHIFT," except it uses a table of size 2gs to perform gs shifts at a time, and a table of size 2gr to perform gr +reductions at at time. The program gf_methods only prints the options 4 4 and 4 8 as arguments for "GROUP." +However, other values of gs and gr are legal and sometimes desirable:

    + +
      +
    1. + For w ≤ 32 and w = 64, any values of gs and gr may be used, so long as they are less than or equal to w and so +long as the tables fit into memory. There are four exceptions to this, listed below .

    2. +
    3. For w = 4, "GROUP" is not supported.

    4. +
    5. For w = 8, "GROUP" is not supported.

    6. +
    7. For w = 16, "GROUP" is only supported for gs = gr = 4.

    8. +
    9. For w = 128 "GROUP" only supports gs = 4 and gr ∈ {4, 8, 16}.

    10. +
    +

    +The way that gs and gr impact performance is as follows. The "SHIFT" implementation works by performing a +carry-free multiplication in w steps, and then performing reduction in w steps. In "GROUP," the carry-free multiplication +is reduced to w /gssteps, and the reduction is reduced to w /gr + +. Both require tables. The table for the carry-free +multiplication must be created at the beginning of each multiply() or multiply_region(), while the table for reduction +is created when the gf_t is initialized. For that reason, it makes sense for gr to be bigger than gs.

    + +

    +To give a flavor for the impact of these arguments, Figure 3 show s the performance of varying gs and gr for +single multiplication and region multiplication respectively, in GF(232) and GF(264). As the graphs demonstrate, +multiply() performs better w ith smaller values of gs, w hile multiply region() amortizes the creation of the shifting +table, and can tolerate larger values of gs. w hen gs equals gr, there are some optimizations that we hand-encode. +These can be seen clearly in the multiply_region() graphs. +

    + + + + + + + + +
    +7     FURTHER INFORMATION ON OPTIONS AND ALGORITHMS 30 + + +
    + +
    + +
    +
    +Figure 3: The performance of multiply() and multiply_region() using "GROUP," and varying the arguments
    gs +and gr. All graphs are heat maps with black equaling zero. The region size is 100KB. + +

    7.6  Considerations with "COMPOSITE"

    + + +As mentioned above, using "ALTMAP" with "COMPOSITE" allows multiply_region() to recursively call multiply_ +region(), rather than simply calling multiply() on every word in the region. The difference can be pronounced:

    + +
    + + + + + + + + + +
    +gf_time 32 G 0 10240 10240 -m COMPOSITE 2 - - +Speed = 322 MB/s
    gf_time 32 G 0 10240 10240 -m COMPOSITE 2 - -r ALTMAP - +Speed = 3,368 MB/s
    +gf_time 32 G 0 10240 10240 -m COMPOSITE 2 -m SPLIT 16 4 -r ALTMAP - -r ALTMAP - +Speed = 3,925 MB/s
    +
    + + +

    +

    +There is support for performing multiply() inline for the "TABLE" implementations for w ∈ {4, 8} and for the +"LOG" implementation for w = 16 (see section 7.1). These are leveraged by multiply() in "COMPOSITE," and +by multiply_region() if you are not using "ALTMAP." To demonstrate this, in the table below, you can see that the +performance of multiply() with "SPLIT 8 4" is 88 percent as fast than the default in w = 8 (which is "TABLE"). +When you use each as a base field for "COMPOSITE" with w = 16, the one with "SPLIT 8 4" is now just 37 percent +as fast. The difference is the inlining of multiplication in the base field when "TABLE" is employed:



    + +
    + + + + + + + + +
    gf_time 8 M 0 1048576 100 - Speed = 501 Mega-ops/s
    gf_time 8 M 0 1048576 100 -m SPLIT 8 4 - Speed = 439 Mega-ops/s
    gf_time 8 M 0 1048576 100 -m COMPOSITE 2 - - Speed = 207 Mega-ops/s
    gf_time 8 M 0 1048576 100 -m COMPOSITE 2 -m SPLIT 8 4 - - Speed = 77 Mega-ops/s
    +
    +

    +
    + +You can keep making recursive definitions of composites field if you want. For example, this one's not too slow for +region operations (641 MB/s): + + + + + + + + +
    +
    + + +6     FURTHER INFORMATION ON OPTIONS AND ALGORITHMS 31


    + +
    +
    +gf_time 128 G 0 1048576 100 -m COMPOSITE 2 -m COMPOSITE 2 -m COMPOSITE 2
    +-m SPLIT 16 4 -r ALTMAP - -r ALTMAP - -r ALTMAP - -r ALTMAP - +
    +

    + +

    Please see section 7.8.1 for a discussion of polynomials in composite fields.

    + +

    7.7       "CARRY_FREE" and the Primitive Polynomial

    + + +If your machine supports the PCLMUL instruction, then we leverage that in "CARRY_FREE." This implementation +first performs a carry free multiplication of two w-bit numbers, which yields a 2w-bit number. It does this with +one PCLMUL instruction. To reduce the 2w-bit number back to a w-bit number requires some manipulation of the +polynomial. As it turns out, if the polynomial has a lot of contiguous zeroes following its leftmost one, the number of +reduction steps may be minimized. For example, with w = 32, we employ the polynomial 0x100400007, because that +is what other libraries employ. This only has 9 contiguous zeros following the one, which means that the reduction +takes four steps. If we instead use 0x1000000c5, which has 24 contiguous zeros, the reduction takes just two steps. +You can see the difference in performance: +

    +
    +
    + + + + + + + + +
    gf_time 32 M 0 1048576 100 -m CARRY_FREE - Speed = 48 Mega-ops/s
    gf_time 32 M 0 1048576 100 -m CARRY_FREE -p 0xc5 - Speed = 81 Mega-ops/s
    + +

    + +

    +This is relevant for w = 16 and w = 32, where the "standard" polynomials are sub-optimal with respect to +"CARRY_FREE." For w = 16, the polynomial 0x1002d has the desired property. It’s less important, of course, +with w = 16, because "LOG" is so much faster than CARRY_FREE.

    + +

    7.8   More on Primitive Polynomials

    + +

    7.8.1   Primitive Polynomials that are not Primitive

    + +The library is willing to work with most polynomials, even if they are not primitive or irreducible. For example, the +polynomial x4 + x3 +x2 +x+1 is irreducible, and therefore generates a valid Galois Field for GF(24). However, it +is not primitive, because 25 = 1. For that reason, if you use this polynomial, you cannot use the "LOG" method. The +other methods will work fine:

    + +
    + +UNIX> gf_mult 2 2 4 -p 0xf -
    +4
    +UNIX> gf_mult 4 2 4 -p 0xf -
    +8
    +UNIX> gf_mult 8 2 4 -p 0xf -
    +15
    +UNIX> gf_mult 15 2 4 -p 0xf -
    +1
    +UNIX> gf_div 1 15 4 -p 0xf -
    +2
    +UNIX> gf_div 1 15 4 -p 0xf -m LOG -
    +usage: gf_div a b w [method] - does division of a and b in GF(2ˆw)
    +Bad Method Specification: Cannot use Log tables because the polynomial is not primitive.
    +UNIX>
    +
    +

    +If a polynomial is reducible, then it does not define a Galois Field, but instead a ring. GF-Complete attempts to +work here where it can; however certain parts of the library will not work: +

    + + + + + + +
    + + +6     FURTHER INFORMATION ON OPTIONS AND ALGORITHMS 32


    +
      +
    1. +Division is a best effort service. The problemis that often quotients are not unique. If divide() returns a non-zero +number, then that number will be a valid quotient, but it may be one of many. If the multiplication technique is +"TABLE," then if a quotient exists, one is returned. Otherwise, zero is returned. Here are some examples - the +polynomial x4 + 1 is reducible, and therefore produces a ring. Below, we see that with this polynomal, 1*6 = 6 +and 14*6 = 6. Therefore, 6/6 has two valid quotients: 1 and 14. GF-Complete returns 14 as the quotient:

    2. + +
      +UNIX> gf_mult 1 6 4 -p 0x1 -
      +6
      +UNIX> gf_mult 14 6 4 -p 0x1 -
      +6
      +UNIX> gf_div 6 6 4 -p 0x1 -
      +14
      +UNIX>

      +
      + + +
    3. When "EUCLID" is employed for division, it uses the extended Euclidean algorithm for GCD to find a number's +inverse, and then it multiplies by the inverse. The problem is that not all numbers in a ring have inverses. For +example, in the above ring, there is no number a such that 6a = 1. Thus, 6 has no inverse. This means that even +though 6/6 has quotients in this ring, "EUCLID" will fail on it because it is unable to find the inverse of 6. It will +return 0: +

    4. +
      +UNIX> gf_div 6 6 4 -p 0x1 -m TABLE -d EUCLID -
      +0
      +UNIX>
      +

      + +
    5. Inverses only work if a number has an inverse. Inverses may not be unique.

    6. + +
    7. "LOG" will not work. In cases where the default would be "LOG," "SHIFT" is used instead.
    8. +
    + +

    +Due to problems with division, gf_unit may fail on a reducible polynomial. If you are determined to use such a +polynomial, don't let this error discourage you. +

    + +

    7.8.2 Default Polynomials for Composite Fields

    + +GF-Complete will successfully select a default polynomial in the following composite fields: +
      +
    • w = 8 and the default polynomial (0x13) is employed for GF(24)

    • +
    • w = 16 and the default polynomial (0x11d) is employed for GF(28)

    • +
    • w = 32 and the default polynomial (0x1100b) is employed for GF(216)

    • +
    • w = 32 and 0x1002d is employed for GF(216)

    • +
    • w = 32 and the base field for GF(w16) is a composite field that uses a default polynomial

    • +
    • w = 64 and the default polynomial (0x100400007) is employed for GF(232)

    • +
    • w = 64 and 0x1000000c5 is employed for GF(232)

    • +
    • w = 64 and the base field for GF(w32) is a composite field that uses a default polynomial

    • +
    • w = 128 and the default polynomial (0x1b) is employed for GF(264)

    • +
    • w = 128 and the base field for GF(w64 ) is a composite field that uses a default polynomial

    • +
    + + + + + + + + +
    + + +6     FURTHER INFORMATION ON OPTIONS AND ALGORITHMS 33


    + + +

    7.8.3 The Program gf_poly for Verifying Irreducibility of Polynomials

    + +The program gf_poly uses the Ben-Or algorithm[GP97] to determine whether a polynomial with coefficients in GF(2w ) +is reducible. Its syntax is:

    +
    +gf_poly w method power:coef power:coef ... +
    + +
    +

    You can use it to test for irreducible polynomials with binary coefficients by specifying w = 1. For example, from +the discussion above, we know that x4 +x+1 and x4 +x3 +x2 +x+1 are both irreducible, but x4 +1 is reducible. +gf_poly confirms:


    + +

    +UNIX> gf_poly 1 - 4:1 1:1 0:1
    +Poly: xˆ4 + x + 1
    +Irreducible.
    +UNIX> gf_poly 1 - 4:1 3:1 2:1 1:1 0:1 +Poly: xˆ4 + xˆ3 + xˆ2 + x + 1
    +Irreducible.
    +UNIX> gf_poly 1 - 4:1 0:1 r
    +Poly: xˆ4 + 1
    +Reducible.
    +UNIX>
    + +
    + + +

    +For composite fields GF((2l)2), we are looking for a value s such that x2 + sx + 1 is irreducible. That value +depends on the base field. For example, for the default field GF(232), a value of s = 2 makes the polynomial +irreducible. However, if the polynomial 0xc5 is used (so that PCLMUL is fast - see section 7.7), then s = 2 yields a +reducible polynomial, but s = 3 yields an irreducible one. You can use gf_poly to help verify these things, and to help +define s if you need to stray from the defaults:


    + +
    +UNIX> gf_poly 32 - 2:1 1:2 0:1
    +Poly: xˆ2 + (0x2)x + 1
    +Irreducible.
    +UNIX> gf_poly 32 -p 0xc5 - 2:1 1:2 0:1
    +Poly: xˆ2 + (0x2)x + 1
    +Reducible.
    +UNIX> gf_poly 32 -p 0xc5 - 2:1 1:3 0:1
    +Poly: xˆ2 + (0x3)x + 1
    +Irreducible.
    +UNIX>
    +
    + +

    +gf_unit does random sampling to test for problems. In particular, it chooses a random a and a random b, multiplies +them, and then tests the result by dividing it by a and b. When w is large, this sampling does not come close to +providing complete coverage to check for problems. In particular, if the polynomial is reducible, there is a good +chance that gf_unit won't discover any problems. For example, the following gf_unit call does not flag any problems, +even though the polynomial is reducible.

    +
    +
    +UNIX> gf_unit 64 A 0 -m COMPOSITE 2 -p 0xc5 - -p 2 -
    +UNIX> +
    + +

    +How can we demonstrate that this particular field has a problem? Well, when the polynomial is 0xc5, we can factor +x2 + 2x + 1 as (x + 0x7f6f95f9)(x + 0x7f6f95fb). Thus, in the composite field, when we multiply 0x17f6f95f9 by +0x17f6f95fb, we get zero. That's the problem: +

    + + + + + + + + +
    + + +6     FURTHER INFORMATION ON OPTIONS AND ALGORITHMS 34


    + +
    + +UNIX> gf_mult 7f6f95f9 7f6f95fb 32h -p 0xc5 -
    +1
    +UNIX> gf_mult 17f6f95f9 17f6f95fb 64h -m COMPOSITE 2 -p 0xc5 - -p 2 -
    +0
    +UNIX>
    + +
    + +

    7.9 "ALTMAP" considerations and extract_word()

    + +There are two times when you may employ alternate memory mappings: +
      +
    1. When using "SPLIT" and wb = 4.
    2. +
    3. When using "COMPOSITE."
    4. +
    + +Additionally, by default, the "CAUCHY" region option also employs an alternate memory mapping. + +

    When you use alternate memory mappings, the exact mapping of words in GF(2w ) to memory depends on the +situation, the size of the region, and the alignment of the pointers. To help you figure things out, we have included the +procedures extract_word.wxx() as part of the gf_t struct. This procedure takes four parameters:

    +
      +
    • A pointer to the gf_t.
    • +
    • The beginning of the memory region.
    • +
    • The number of bytes in the memory region.
    • +
    • The desired word number: n.
    • +
    + +

    +It then returns the n-th word in memory. When the standard mapping is employed, this simply returns the n- +th contiguous word in memory. With alternate mappings, each word may be split over several memory regions, so +extract_word() grabs the relevant parts of each memory region to extract the word. Below, we go over each of the +above situations in detail. Please refer to Figure 2 in Section 5 for reference.

    + + +

    7.9.1 Alternate mappings with "SPLIT"

    + +The alternate mapping with "SPLIT" is employed so that we can best leverage mm_shuffle_epi8(). Please read [PGM13b] +for details as to why. Consider an example when w = 16. In the main region of memory (the middle region in Figure +2), multiplication proceeds in units of 32 bytes, which are each broken into two 16-byte regions. The first region +holds the high bytes of each word in GF(216), and the second region holds the low bytes. +Let's look at a very detailed example, from gf_example_5.c. This program makes the following call, where gf has + +been initialized for w = 16, using "SPLIT" and "ALTMAP:"

    +
    +gf.multiply_region.w32(&gf, a, b, 0x1234, 30*2, 0); +

    + + +

    In other words, it is multiplying a region a of 60 bytes (30 words) by the constant 0x1234 in GF(216), and placing +the result into b. The pointers a and b have been set up so that they are not multiples of 16. The first line of output +prints a and b:


    + +a: 0x10010008c b: 0x10010015c

    + +As described in Section 5, the regions of memory are split into three parts: + + + + + + + + +
    + + +6     FURTHER INFORMATION ON OPTIONS AND ALGORITHMS 35


    + + +
      +
    1. 4 bytes starting at 0x1001008c / 0x10010015c.
    2. +
    3. 32 bytes starting at 0x10010090 / 0x100100160.
    4. +
    5. 24 bytes starting at 0x100100b0 / 0x100100180.
    6. + +
    + + +

    In the first and third parts, the bytes are laid out according to the standard mapping. However, the second part is +split into two 16-byte regions- one that holds the high bytes of each word and one that holds the low bytes. To help +illustrate, the remainder of the output prints the 30 words of a and b as they appear in memory, and then the 30 return +values of extract_word.w32():


    + +
    + + + + + + + + + + + +
    1 2 3 4 5 6 7 8 9
    a: 640b 07e5 2fba ce5d f1f9 3ab8 c518 1d97 45a7 0160
    b: 1ba3 644e 84f8 be3c 4318 4905 b2fb 46eb ef01 a503
    +

    + + + + + + + + + + + +
    10 11 12 13 14 15 16 17 18 19
    a: 3759 b107 9660 3fde b3ea 8a53 75ff 46dc c504 72c2
    b: da27 e166 a0d2 b3a2 1699 3a3e 47fb 39af 1314 8e76
    + + +

    + + + + + + + + + +
    20 21 22 23 24 25 26 27 28 29
    a: b469 1b97 e91d 1dbc 131e 47e0 c11a 7f07 76e0 fe86
    b: 937c a5db 01b7 7f5f 8974 05e1 cff3 a09c de3c 4ac0
    +

    + + + + + + + + + + + + + + + + + + + +
    Word 0: 0x640b * 0x1234 = 0x1ba3 Word 15: 0x4575 * 0x1234 = 0xef47
    Word 1: 0x07e5 * 0x1234 = 0x644e Word 16: 0x60dc * 0x1234 = 0x03af
    Word 2: 0xba59 * 0x1234 = 0xf827 Word 17: 0x0146 * 0x1234 = 0xa539
    Word 3: 0x2f37 * 0x1234 = 0x84da Word 18: 0xc504 * 0x1234 = 0x1314
    Word 4: 0x5d07 * 0x1234 = 0x3c66 Word 19: 0x72c2 * 0x1234 = 0x8e76
    Word 5: 0xceb1 * 0x1234 = 0xbee1 Word 20: 0xb469 * 0x1234 = 0x937c
    Word 6: 0xf960 * 0x1234 = 0x18d2 Word 21: 0x1b97 * 0x1234 = 0xa5db
    Word 7: 0xf196 * 0x1234 = 0x43a0 Word 22: 0xe91d * 0x1234 = 0x01b7
    Word 8: 0xb8de * 0x1234 = 0x05a2 Word 23: 0x1dbc * 0x1234 = 0x7f5f
    Word 9: 0x3a3f * 0x1234 = 0x49b3 Word 24: 0x131e * 0x1234 = 0x8974
    Word 10: 0x18ea * 0x1234 = 0xfb99 Word 25: 0x47e0 * 0x1234 = 0x05e1
    Word 11: 0xc5b3 * 0x1234 = 0xb216 Word 26: 0xc11a * 0x1234 = 0xcff3
    Word 12: 0x9753 * 0x1234 = 0xeb3e Word 27: 0x7f07 * 0x1234 = 0xa09c
    Word 13: 0x1d8a * 0x1234 = 0x463a Word 28: 0x76e0 * 0x1234 = 0xde3c
    Word 14: 0xa7ff * 0x1234 = 0x01fb Word 29: 0xfe86 * 0x1234 = 0x4ac0
    +
    +
    +In the first region are words 0 and 1, which are identical to how they appear in memory: 0x640b and 0x07e5. In +the second region are words 2 through 17. These words are split among the two sixteen-byte regions. For example, +word 2, which extract_word() reports is 0xba59, is constructed from the low byte in word 2 (0xba) and the low byte +in word 10 (0x59). Since 0xba59 * 0x1234 = 0xf827, we see that the low byte in word 2 of b is 0xf8, and the low byte +in word 10 is 0x27. +

    When we reach word 22, we are in the third region of memory, and words are once again identical to how they +appear in memory.

    + +

    While this is confusing, we stress that that so long as you call multiply_region() with pointers of the same alignment +and regions of the same size, your results with ALTMAP will be consistent. If you call it with pointers of

    + + + + + + +
    + + +7     FURTHER INFORMATION ON OPTIONS AND ALGORITHMS 36


    + +different alignments, or with different region sizes, then the results will not be consistent. To reiterate, if you don't use +ALTMAP, you don't have to worry about any of this - words will always be laid out contiguously in memory. +

    +When w = 32, the middle region is a multiple of 64, and each word in the middle region is broken into bytes, each +of which is in a different 16-byte region. When w = 64, the middle region is a multiple of 128, and each word is +stored in eight 16-byte regions. And finally, whenw = 128, the middle region is a multiple of 128, and each word is +stored in 16 16-byte regions.


    + +

    7.9.2   Alternate mappings with "COMPOSITE"

    + +With "COMPOSITE," the alternate mapping divides the middle region in half. The lower half of each word is stored +in the first half of the middle region, and the higher half is stored in the second half. To illustrate, gf_example_6 +performs the same example as gf_example_5, except it is using "COMPOSITE" in GF((216)2), and it is multiplying +a region of 120 bytes rather than 60. As before, the pointers are not aligned on 16-bit quantities, so the region is broken +into three regions of 4 bytes, 96 bytes, and 20 bytes. In the first and third region, each consecutive four byte word is a +word in GF(232). For example, word 0 is 0x562c640b, and word 25 is 0x46bc47e0. In the middle region, the low two +bytes of each word come from the first half, and the high two bytes come from the second half. For example, word 1 +as reported by extract_word() is composed of the lower two bytes of word 1 of memory (0x07e5), and the lower two +bytes of word 13 (0x3fde). The product of 0x3fde07e5 and 0x12345678 is 0x211c880d, which is stored in the lower +two bytes of words 1 and 13 of b.

    + +a: 0x10010011c b: 0x1001001ec + +

    + +
    + + + + + + + + + + + + + + +
    1 2 3 4 5 6 7 8 9
    a: 562c640b 959407e5 56592fba cbadce5d 1d1cf1f9 35d73ab8 6493c518 b37c1d97 8e4545a7 c0d80160
    b: f589f36c f146880d 74f7b349 7ea7c5c6 34827c1a 93cc3746 bfd9288b 763941d1 bcd33a5d da695e64
    + + +

    + + + + + + + + + + + + + +
    10 11 12 13 14 15 16 17 18 19
    a: 965b3759 cb3eb107 1b129660 95a33fde 95a7b3ea d16c8a53 153375ff f74646dc 35aac504 98f972c2
    b: fd70f125 3274fa8f d9dd34ee c01a211c d4402403 8b55c08b da45f0ad 90992e18 b65e0902 d91069b5
    + + + +

    + + + + + + + + + + + +
    20 21 22 23 24 25 26 27 28 29
    a: 5509b469 7f8a1b97 3472e91d 9ee71dbc de4e131e 46bc47e0 5bc9c11a 931d7f07 c85cfe86 fe86
    b: fc92b8f5 edd59668 b4bc0d90 a679e4ce 1a98f7d0 6038765f b2ff333f e7937e49 fa5a5867 79c00ea2
    +

    + + + + + + + + + + + + + + + + + + + +
    Word 0: 0x562c640b * 0x12345678 = 0xf589f36c Word 15: 0xb46945a7 * 0x12345678 = 0xb8f53a5d
    Word 1: 0x3fde07e5 * 0x12345678 = 0x211c880d Word 16: 0x55098e45 * 0x12345678 = 0xfc92bcd3
    Word 2: 0x95a39594 * 0x12345678 = 0xc01af146 Word 17: 0x1b970160 * 0x12345678 = 0x96685e64
    Word 3: 0xb3ea2fba * 0x12345678 = 0x2403b349 Word 18: 0x7f8ac0d8 * 0x12345678 = 0xedd5da69
    Word 4: 0x95a75659 * 0x12345678 = 0xd44074f7 Word 19: 0xe91d3759 * 0x12345678 = 0x0d90f125
    Word 5: 0x8a53ce5d * 0x12345678 = 0xc08bc5c6 Word 20: 0x3472965b * 0x12345678 = 0xb4bcfd70
    Word 6: 0xd16ccbad * 0x12345678 = 0x8b557ea7 Word 21: 0x1dbcb107 * 0x12345678 = 0xe4cefa8f
    Word 7: 0x75fff1f9 * 0x12345678 = 0xf0ad7c1a Word 22: 0x9ee7cb3e * 0x12345678 = 0xa6793274
    Word 8: 0x15331d1c * 0x12345678 = 0xda453482 Word 23: 0x131e9660 * 0x12345678 = 0xf7d034ee
    Word 9: 0x46dc3ab8 * 0x12345678 = 0x2e183746 Word 24: 0xde4e1b12 * 0x12345678 = 0x1a98d9dd
    Word 10: 0xf74635d7 * 0x12345678 = 0x909993cc Word 25: 0x46bc47e0 * 0x12345678 = 0x6038765f
    Word 11: 0xc504c518 * 0x12345678 = 0x0902288b Word 26: 0x5bc9c11a * 0x12345678 = 0xb2ff333f
    Word 12: 0x35aa6493 * 0x12345678 = 0xb65ebfd9 Word 27: 0x931d7f07 * 0x12345678 = 0xe7937e49
    +
    + + + + + + + + +
    + + +8     THREAD SAFETY 37


    +
    + + + + + + + + + +
    Word 13: 0x72c21d97 * 0x12345678 = 0x69b541d1 Word 28: 0xd40676e0 * 0x12345678 = 0xfa5a5867
    Word 14: 0x98f9b37c * 0x12345678 = 0xd9107639 Word 29: 0xc85cfe86 * 0x12345678 = 0x79c00ea2
    +

    + + +

    +As with "SPLIT," using multiply_region() with "COMPOSITE" and "ALTMAP" will be consistent only if the +alignment of pointers and region sizes are identical.

    + + +

    7.9.3 The mapping of "CAUCHY"

    + +With "CAUCHY," the region is partitioned into w subregions, and each word in the region is broken into w bits, +each of which is stored in a different subregion. To illustrate, gf_example_7 multiplies a region of three bytes by 5 +in GF(23) using "CAUCHY:"

    + +
    + +UNIX> gf_example_7
    +a: 0x100100190 b: 0x1001001a0

    +a: 0x0b 0xe5 0xba
    +b: 0xee 0xba 0x0b

    +a bits: 00001011 11100101 10111010
    +b bits: 11101110 10111010 00001011

    +Word 0: 3 * 5 = 4
    +Word 1: 5 * 5 = 7
    +Word 2: 2 * 5 = 1
    +Word 3: 5 * 5 = 7
    +Word 4: 4 * 5 = 2
    +Word 5: 6 * 5 = 3
    +Word 6: 2 * 5 = 1
    +Word 7: 6 * 5 = 3
    +UNIX>

    +

    + +The program prints the three bytes of a and b in hexadecimal and in binary. To see how words are broken up, +consider word 0, which is the lowest bit of each of the three bytes of a (and b). These are the bits 1, 1 and 0 in a, and +0, 0, and 1 in b. Accordingly, the word is 3 in a, and 3*5 = 4 in b. Similarly, word 7 is the high bit in each byte: 0, 1, 1 +(6) in a, and 1, 1, 0 (3) in b.

    +

    With "CAUCHY," multiply_region()may be implemented exclusively with XOR operations. Please see [BKK+95] +for more information on the motivation behind "CAUCHY."

    + +

    8   Thread Safety

    + +Once you initialize a gf_t, you may use it wontonly in multiple threads for all operations except for the ones below. +With the implementations listed below, the scratch space in the gf_t is used for temporary tables, and therefore you +cannot call region_multiply, and in some cases multiply from multiple threads because they will overwrite each +others' tables. In these cases, if you want to call the procedures from multiple threads, you should allocate a separate +gf_t for each thread: +
      +
    • + All "GROUP" implementations are not thread safe for either region_multiply() or multiply(). Other than +"GROUP," multiply() is always thread-safe. + +
    • +
    + + + + + + + + + +
    + + +9     LISTING OF PROCEDURES 38


    +
      +
    • + +For w = 4, region_multiply.w32() is unsafe in in "-m TABLE -r QUAD -r LAZY."

    • +
    • For w = 8, region_multiply.w32() is unsafe in in "-m TABLE -r DOUBLE -r LAZY."

    • +
    • For w = 16, region_multiply.w32() is unsafe in in "-m TABLE."

    • +
    • For w ∈ {32, 64, 128}, all "SPLIT" implementations are unsafe for region_multiply(). This means that if the +default uses "SPLIT" (see Table 1 for when that occurs), then region_multiply() is not thread safe.

    • +
    • The "COMPOSITE" operations are only safe if the implementations of the underlying fields are safe.
    • +
    + +

    9  Listing of Procedures

    + +The following is an alphabetical listing of the procedures, data types and global variables for users to employ in +GF-complete.
    + +
      +
    • GF_W16_INLINE_DIV() in gf_complete.h: This is a macro for inline division when w = 16. See section 7.1.

    • +
    • GF_W16_INLINE_MULT() in gf_complete.h: This is a macro for inline multiplication when w = 16. See +section 7.1.

    • +
    • GF_W4_INLINE_MULTDIV() in gf_complete.h: This is a macro for inline multiplication/division when w = +4. See section 7.1.

    • + +
    • GF_W8_INLINE_MULTDIV() in gf_complete.h: This is a macro for inline multiplication/division when w = +8. See section 7.1.

    • +
    • MOA_Fill_Random_Region() in gf_rand.h: Fills a region with random numbers.

    • +
    • MOA_Random_128() in gf_rand.h: Creates a random 128-bit number.

    • +
    • MOA_Random_32() in gf_rand.h: Creates a random 32-bit number.

    • +
    • MOA_Random_64() in gf_rand.h: Creates a random 64-bit number.

    • +
    • MOA_Random_W() in gf_rand.h: Creates a random w-bit number, where w ≤ 32.

    • +
    • MOA_Seed() in gf_rand.h: Sets the seed for the random number generator.

    • +
    • gf_errno in gf_complete.h: This is to help figure out why an initialization call failed. See section 6.1.

    • +
    • gf_create_gf_from_argv() in gf_method.h: Creates a gf_t using C style argc/argv. See section 6.1.1.

    • +
    • gf_division_type_t in gf_complete.h: the different ways to specify division when using gf_init_hard(). See +section 6.4.

    • +
    • gf_error() in gf_complete.h: This prints out why an initialization call failed. See section 6.1.

    • + +
    • gf_extract in gf_complete.h: This is the data type of extract_word() in a gf_t. See section 7.9 for an example +of how to use extract word().
    • +
    + + + + + +
    + + +9     LISTING OF PROCEDURES 39


    +
      +
    • +gf_free() in gf_complete.h: If gf_init easy(), gf_init hard() or create_gf_from_argv() allocated memory, this +frees it. See section 6.4.
    • + +
    • gf_func_a_b in gf_complete.h: This is the data type of multiply() and divide() in a gf_t. See section 4.2 for +examples of how to use multiply() and divide()

    • + +
    • gf_func_a_b in gf_complete.h: This is the data type of multiply() and divide() in a gf_t. See section 4.2 for +examples of how to use multiply() and divide()

    • + +
    • gf_func_a in gf_complete.h: This is the data type of inverse() in a gf_t

    • + +
    • gf_general_add() in gf_general.h: This adds two gf_general_t's

    • + +
    • gf_general_divide() in gf_general.h: This divides two gf_general t's

    • + +
    • gf_general_do_region_check() in gf_general.h: This checks a region multiply of gf_general_t's

    • + +
    • gf_general_do_region_multiply() in gf_general.h: This does a region multiply of gf_general_t's

    • + +
    • gf_general_do_single_timing_test() in gf_general.h: Used in gf_time.c

    • + +
    • gf_general_inverse() in gf_general.h: This takes the inverse of a gf_general_t

    • + +
    • gf_general_is_one() in gf_general.h: This tests whether a gf_general_t is one

    • + +
    • gf_general_is_two() in gf_general.h: This tests whether a gf_general_t is two

    • + +
    • gf_general_is_zero() in gf_general.h: This tests whether a gf_general_t is zero

    • + +
    • gf_general_multiply() in gf_general.h: This multiplies two gf_general_t's. See the implementation of gf_mult.c + +for an example

    • +
    • gf_general_s_to_val() in gf_general.h: This converts a string to a gf_general t. See the implementation of +gf_mult.c for an example

    • +
    • gf_general_set_one() in gf_general.h: This sets a gf_general_t to one

    • +
    • gf_general_set_random() in gf_general.h: This sets a gf_general_t to a random number

    • +
    • gf_general_set_two() in gf_general.h: This sets a gf_general_t to two

    • +
    • gf_general_set_up_single_timing_test() in gf_general.h: Used in gf_time.c

    • +
    • gf_general_set_zero() in gf_general.h: This sets a gf_general_t_to_zero

    • +
    • gf_general_t_in .gf_general.h: This is a general data type for all values of w. See the implementation of gf_mult.c +for examples of using these

    • +
    • gf_general_val_to_s() ingf_general.h: This converts a gf_general_t to a string. See the implementation of +gf_mult.c for an example

    • + +
    • gf_init_easy() in gf_complete.h: This is how you initialize a default gf_t. See 4.2 through 4.5 for examples of +calling gf_init_easy()

    • +
    + + + + + + + +
    + + +9     LISTING OF PROCEDURES 40


    + +
      + +
    • gf_init hard() in gf_complete.h: This allows you to initialize a gf_t without using the defaults. See 6.4. We +recommend calling create gf_from argv() when you can, instead of gf_ init_hard()

    • + +
    • gf_ mult_type_t in gf_complete.h: the different ways to specify multiplication when using gf_init hard(). See +section 6.4

    • + +
    • gf_region_type_t in gf_complete.h: the different ways to specify region multiplication when using gf_init_hard(). +See section 6.4

    • + +
    • gf_region_in gf_complete.h: This is the data type of multiply_region() in a gf_t. See section 4.3 for an example +of how to use multiply_region()

    • + +
    • gf_scratch_size() in gf_complete.h: This is how you calculate how much memory a gf_t needs. See section 6.4.

    • + +
    • gf_size() in gf_complete.h: Returns the memory consumption of a gf_t. See section 6.5.

    • + +
    • gf_ val_128_t in gf_complete.h: This is how you store a value where w ≤ 128. It is a pointer to two 64-bit +unsigned integers. See section 4.4

    • + + +
    • gf_val_32_t in gf_ complete.h: This is how you store a value where w ≤ 32. It is equivalent to a 32-bit unsigned +integer. See section 4.2

    • + +
    • gf_ val_64_t in gf_complete.h: This is how you store a value where w ≤ 64. It is equivalent to a 64-bit unsigned +integer. See section 4.5

    • + +
    • gf_w16_get_div_alog_table() in gf_ complete.h: This returns a pointer to an inverse logarithm table that can be +used for inlining division when w = 16. See section 7.1

    • + + +
    • gf_w16_get_log_table() in gf_complete.h: This returns a pointer to a logarithm table that can be used for inlining +when w = 16. See section 7.1

    • + + +
    • gf_w16_get_mult_alog_table() in gf_complete.h: This returns a pointer to an inverse logarithm table that can be +used for inlining multiplication when w = 16. See section 7.1

    • + + +
    • gf_ w4 get div table() in gf_complete.h: This returns a pointer to a division table that can be used for inlining +when w = 4. See section 7.1

    • + + +
    • gf_w4_get_mult_table() in gf_complete.h: This returns a pointer to a multiplication table that can be used for +inlining when w = 4. See section 7.1

    • + +
    • gf_w8_get_div_table() in gf_complete.h: This returns a pointer to a division table that can be used for inlining +when w = 8. See section 7.1

    • + +
    • gf_w8_get_mult_table() in gf_complete.h: This returns a pointer to a multiplication table that can be used for +inlining when w = 8. See section 7.1

    • + +
    + + + + + + + + + +
    +10     TROUBLESHOOTING 41


    + +
      +
    • SSE support. Leveraging SSE instructions requires processor support as well as compiler support. For example, +the Mac OS 10.8.4 (and possibly earlier versions) default compile environment fails to properly compile +PCLMUL instructions. This issue can be fixed by installing an alternative compiler; see Section 3 for details

    • + +
    • Initialization segfaults. You have to already have allocated your gf_t before you pass a pointer to it in +bgf_init_easy(), create_gf_ from_argv(), or bgf_ini_hard()

    • + + +
    • GF-Complete is slower than it should be. Perhaps your machine has SSE, but you haven't specified the SSE +compilation flags. See section 3 for how to compile using the proper flags

    • + + +
    • Bad alignment. If you get alignment errors, see Section 5

    • + +
    • Mutually exclusive region types. Some combinations of region types are invalid. All valid and implemented +combinations are printed by bgf_methods.c

    • + +
    • Incompatible division types. Some choices of multiplication type constrain choice of divide type. For example, +"COMPOSITE" methods only allow the default division type, which divides by finding inverses (i.e., +neither "EUCLID" nor "MATRIX" are allowed). For each multiplication method printed by gf_methods.c, the +corresponding valid division types are also printed

    • + + +
    • Arbitrary "GROUP" arguments. The legal arguments to "GROUP" are specified in section 7.5

    • + +
    • Arbitrary "SPLIt" arguments. The legal arguments to "SPLIt" are specified in section 7.4

    • + +
    • Threading problems. For threading questions, see Section 8

    • + +
    • No default polynomial. If you change the polynomial in a base field using "COMPOSITE," then unless it is +a special case for which GF-Complete finds a default polynomial, you'll need to specify the polynomial of the +composite field too. See 7.8.2 for the fields where GF-Complete will support default polynomials

    • +
    • Encoding/decoding with different fields. Certain fields are not compatible. Please see section 7.2 for an +explanation

    • + + +
    • "ALTMAP" is confusing. We agree. Please see section 7.9 for more explanation.

    • + +
    • I used "ALTMAP" and it doesn't appear to be functioning correctly. With 7.9, the size of the region and +its alignment both matter in terms of how "ALTMAP" performs multiply_region(). Please see section 7.9 for +detailed explanation

    • + +
    • Where are the erasure codes?. This library only implements Galois Field arithmetic, which is an underlying +component for erasure coding. Jerasure will eventually be ported to this library, so that you can have fast erasure +coding

    • +
    +

    11     Timings

    + +We don't want to get too detailed with timing, because it is quite machine specific. However, here are the timings on +an Intel Core i7-3770 CPU running at 3.40 GHz, with 4 × 256 KB L2 caches and an 8MB L3 cache. All timings are +obtained with gf_time or gf_inline_time, in user mode with the machine dedicated solely to running these jobs. + + + + + + + + + +
    +10     TROUBLESHOOTING 41


    + +
    +
    Figure 4: Speed of doing single multiplications for w ∈ {4, 8, 16}.
    +

    11.1   Multiply()

    + +The performance of multiply() is displayed in Figures 4 for w ∈ {4, 8, 16} and 5 for w ∈ {32, 64, 128}. These +numbers were obtained by calling gf_time with the size and iterations both set to 10240. We plot the speed in megaops +per second. + +

    As would be anticipated, the inlined operations (see section 7.1) outperform the others. Additionally, in all +cases with the exception of w = 32, the defaults are the fastest performing implementations. With w = 32, +"CARRY_FREE" is the fastest with an alternate polynomial (see section 7.7). Because we require the defaults to +use a "standard" polynomial, we cannot use this implementation as the default.

    + +

    11.2   Divide()

    + +For the "TABLE" and "LOG" implementations, the performance of division is the same as multiplication. This means +that for w ∈ {4, 8, 16}, it is very fast indeed. For the other implementations, division is implemented with Euclid's +method, and is several factors slower than multiplication. +In Figure 6, we plot the speed of a few implementations of the larger word sizes. Compared to the "TABLE" and +"LOG" implemenations for the smaller word sizes, where the speeds are in the hundreds of mega-ops per second, +these are very slow. Of note is the "COMPOSITE" implementation for w = 32, which is much faster than the others + + + + + + + + +
    +10     TROUBLESHOOTING 43


    + +
    + +
    Figure 5: Speed of doing single multiplications for w ∈ {32, 64, 128}.

    + +because it uses a special application of Euclid's method, which relies on division in GF(216), which is very fast.

    + +

    11.3   Multiply_Region()

    + +Tables 3 through 8 show the performance of the various region operations. It should be noted that for GF(216 ) +through GF(2128), the default is not the fastest implementation of multiply_region(). The reasons for this are outlined +in section 6 +

    +For these tables, we performed 1GB worth of multiply_region() calls for all regions of size 2i bytes for 10 ≤ i ≤ +30. In the table, we plot the fastest speed obtained.

    +

    We note that the performance of "CAUCHY" can be improved with techniques from [LSXP13] and [PSR12].

    + + + + + + + + + +
    +REFERENCES 44


    + +
    + +
    Figure 6: Speed of doing single divisions for w ∈ {32, 64, 128}.

    + +
    +
    + + + + + + + + + + + + + + + + + + + + +
    Method Speed (MB/s)
    -m TABLE (Default) - 11879.909
    -m TABLE -r CAUCHY - 9079.712
    -m BYTWO_b - 5242.400
    -m BYTWO_p - 4078.431
    -m BYTWO_b -r NOSSE - 3799.699
    -m TABLE -r QUAD - 3014.315
    -m TABLE -r DOUBLE - 2253.627
    -m TABLE -r NOSSE - 2021.237
    -m TABLE -r NOSSE - 1061.497
    -m LOG - 503.310
    m SHIFT - 157.749
    -m CARRY_FREE - 86.202


    +
    +
    Table 3: Speed of various calls to multiply_region() for w = 4.
    + +

    References

    + +[Anv09] H. P. Anvin. The mathematics of RAID-6. http://kernel.org/pub/linux/kernel/people/hpa/ +raid6.pdf, 2009.

    + +[BKK+95] J. Blomer, M. Kalfane, M. Karpinski, R. Karp, M. Luby, and D. Zuckerman. An XOR-based erasureresilient +coding scheme. Technical Report TR-95-048, International Computer Science Institute, August +1995.

    + +[GMS08] K. Greenan, E. Miller, and T. J. Schwartz. Optimizing Galois Field arithmetic for diverse processor +architectures and applications. In MASCOTS 2008: 16th IEEE Symposium on Modeling, Analysis and +Simulation of Computer and Telecommunication Systems, Baltimore, MD, September 2008.

    + + +[GP97] S. Gao and D. Panario. Tests and constructions of irreducible polynomials over finite fields. In Foundations +of Computational Mathematics, pages 346–361. Springer Verlag, 1997. + + + + + + + + + + + + + + + + +
    +REFERENCES 45


    + + +
    +
    + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
    Method Speed (MB/s)
    -m SPLIT 8 4 (Default) 13279.146
    -m COMPOSITE 2 - -r ALTMAP - 5516.588
    -m TABLE -r CAUCHY - 4968.721
    -m BYTWO_b - 2656.463
    -m TABLE -r DOUBLE - 2561.225
    -m TABLE - 1408.577
    -m BYTWO_b -r NOSSE - 1382.409
    -m BYTWO_p - 1376.661
    -m LOG_ZERO_EXT - 1175.739
    -m LOG_ZERO - 1174.694
    -m LOG - 997.838
    -m SPLIT 8 4 -r NOSSE - 885.897
    -m BYTWO_p -r NOSSE - 589.520
    -m COMPOSITE 2 - - 327.039
    -m SHIFT - 106.115
    -m CARRY_FREE - 104.299


    +
    +
    Table 4: Speed of various calls to multiply region() for w = 4.


    + +[LBOX12] J. Luo, K. D. Bowers, A. Oprea, and L. Xu. Efficient software implementations of large finite fields +GF(2n) for secure storage applications. ACM Transactions on Storage, 8(2), February 2012.

    + +[LD00] J. Lopez and R. Dahab. High-speed software multiplication in f2m. In Annual International Conference +on Cryptology in India, 2000.

    + +[LHy08] H. Li and Q. Huan-yan. Parallelized network coding with SIMD instruction sets. In International Symposium +on Computer Science and Computational Technology, pages 364-369. IEEE, December 2008.

    + +[LSXP13] J. Luo, M. Shrestha, L. Xu, and J. S. Plank. Efficient encoding schedules for XOR-based erasure codes. +IEEE Transactions on Computing,May 2013.

    + +[Mar94] G. Marsaglia. The mother of all random generators. ftp://ftp.taygeta.com/pub/c/mother. +c, October 1994.
    + +[PGM13a] J. S. Plank, K. M. Greenan, and E. L. Miller. A complete treatment of software implementations of +finite field arithmetic for erasure coding applications. Technical Report UT-CS-13-717, University of +Tennessee, September 2013.

    + +[PGM13b] J. S. Plank, K. M. Greenan, and E. L. Miller. Screaming fast Galois Field arithmetic using Intel SIMD +instructions. In FAST-2013: 11th Usenix Conference on File and Storage Technologies, San Jose, February +2013.

    + +[Pla97] J. S. Plank. A tutorial on Reed-Solomon coding for fault-tolerance in RAID-like systems. Software - +Practice & Experience, 27(9):995-1012, September 1997. + + + + + + + + + + + + +
    +REFERENCES 46


    + + +
    +
    + + + + + + + + + + + + + + + + + + + + + + + + + +
    Method Speed (MB/s)
    -m SPLIT 16 4 -r ALTMAP - 10460.834
    -m SPLIT 16 4 -r SSE (Default) - 8473.793
    -m COMPOSITE 2 - -r ALTMAP - 5215.073
    -m LOG -r CAUCHY - 2428.824
    -m TABLE - 2319.129
    -m SPLIT 16 8 - 2164.111
    -m SPLIT 8 8 - 2163.993
    -m SPLIT 16 4 -r NOSSE - 1148.810
    -m LOG - 1019.896
    -m LOG_ZERO - 1016.814
    -m BYTWO_b - 738.879
    -m COMPOSITE 2 - - 596.819
    -m BYTWO_p - 560.972
    -m GROUP 4 4 - 450.815
    -m BYTWO_b -r NOSSE - 332.967
    -m BYTWO_p -r NOSSE - 249.849
    -m CARRY_FREE - 111.582
    -m SHIFT - 95.813


    +
    +
    Table 5: Speed of various calls to multiply region() for w = 4.


    + +[PMG+13] J. S. Plank, E. L. Miller, K. M. Greenan, B. A. Arnold, J. A. Burnum, A. W. Disney, and A. C. McBride. +GF-Complete: A comprehensive open source library for Galois Field arithmetic. version 1.0. Technical +Report UT-CS-13-716, University of Tennessee, September 2013.

    + +[PSR12] J. S. Plank, C. D. Schuman, and B. D. Robison. Heuristics for optimizing matrix-based erasure codes for +fault-tolerant storage systems. In DSN-2012: The International Conference on Dependable Systems and +Networks, Boston, MA, June 2012. IEEE.

    + +[Rab89] M. O. Rabin. Efficient dispersal of information for security, load balancing, and fault tolerance. Journal +of the Association for Computing Machinery, 36(2):335-348, April 1989. + + + + + + + + + +
    +REFERENCES 47


    +
    +
    + + + + + + + + + + + + +
    Method Speed (MB/s)
    + +-m SPLIT 32 4 -r SSE -r ALTMAP -
    +-m SPLIT 32 4 (Default)
    +-m COMPOSITE 2 -m SPLIT 16 4 -r ALTMAP - -r ALTMAP -
    +-m COMPOSITE 2 - -r ALTMAP -
    +-m SPLIT 8 8 -
    +-m SPLIT 32 8 -
    +-m SPLIT 32 16 -
    +-m SPLIT 8 8 -r CAUCHY
    +-m SPLIT 32 4 -r NOSSE
    +-m CARRY_FREE -p 0xc5
    +-m COMPOSITE 2 -
    +-m BYTWO_b -
    +-m BYTWO_p -
    +-m GROUP 4 8 -
    +-m GROUP 4 4 -
    +-m CARRY_FREE -
    +-m BYTWO_b -r NOSSE -
    +-m BYTWO_p -r NOSSE -
    +-m SHIFT -
    + +
    +7185.440
    +5063.966
    + 4176.440
    +3360.860
    +1345.678
    +1340.656
    +1262.676
    +1143.263
    + 480.859
    +393.185
    +332.964
    +309.971
    +258.623
    +242.076
    +227.399
    +226.785
    +143.403
    +111.956
    +52.295
    +


    +
    +
    Table 6: Speed of various calls to multiply region() w = 4.


    + +
    +
    + + + + + + + + + + + + +
    Method Speed (MB/s)
    +-m SPLIT 64 4 -r ALTMAP -
    +-m SPLIT 64 4 -r SSE (Default) -
    +-m COMPOSITE 2 -m SPLIT 32 4 -r ALTMAP - -r ALTMAP -
    +-m COMPOSITE 2 - -r ALTMAP -
    +-m SPLIT 64 16 -
    +-m SPLIT 64 8 -
    +-m CARRY_FREE -
    +-m SPLIT 64 4 -r NOSSE -
    +-m GROUP 4 4 -
    +-m GROUP 4 8 -
    +-m BYTWO_b -
    +-m BYTWO_p -
    +-m SPLIT 8 8 -
    +-m BYTWO_p -r NOSSE -
    +-m COMPOSITE 2 - -
    +-m BYTWO_b -r NOSSE -
    +-m SHIFT -
    + +
    3522.798
    + 2647.862
    +2461.572
    +1860.921
    +1066.490
    +998.461
    +975.290
    +545.479
    +230.137
    +153.947
    +144.052
    +124.538
    +98.892
    +77.912
    +77.522
    +36.391
    +25.282
    +


    +
    +
    Table 7: Speed of various calls to multiply region() for w = 4.


    + + + + + + + + + + + + + +
    +REFERENCES 48


    + +
    +
    + + + + + + + + + + + + +
    Method Speed (MB/s)
    + +-m SPLIT 128 4 -r ALTMAP -
    +-m COMPOSITE 2 -m SPLIT 64 4 -r ALTMAP - -r ALTMAP -
    +-m COMPOSITE 2 - -r ALTMAP -
    +-m SPLIT 128 8 (Default) -
    +-m CARRY_FREE -
    +-m SPLIT 128 4 -
    +-m COMPOSITE 2 -
    +-m GROUP 4 8 -
    +-m GROUP 4 4 -
    +-m BYTWO_p -
    +-m BYTWO_b -
    +-m SHIFT -
    +
    +1727.683
    +1385.693
    +1041.456
    +872.619
    +814.030
    +500.133
    +289.207
    +133.583
    +116.187
    +25.162
    +25.157
    +14.183
    +


    +
    +
    Table 8: Speed of various calls to multiply region() for w = 4.


    diff --git a/IDA_new/gf-complete/manual/image1.png b/IDA_new/gf-complete/manual/image1.png new file mode 100644 index 0000000000000000000000000000000000000000..c0f7d9511f6d3fefb5c547d16091f682e3ac8de8 GIT binary patch literal 32752 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%0DDbCkL+#x5T^% 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

    p-&rgA(u8U&y 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 CWIB5Q literal 0 HcmV?d00001 diff --git a/IDA_new/gf-complete/manual/image4.png b/IDA_new/gf-complete/manual/image4.png new file mode 100644 index 0000000000000000000000000000000000000000..ba6cf0780d5863ede819bd841ddfead5f8c27a4d GIT binary patch literal 26886 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 literal 0 HcmV?d00001 diff --git a/IDA_new/gf-complete/manual/image5.png b/IDA_new/gf-complete/manual/image5.png new file mode 100644 index 0000000000000000000000000000000000000000..0e169dd718a0bf88a44a0efa553f2f650e1313b0 GIT binary patch literal 51457 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 V`Y=1wixyRBsOjALaML{SKLAk1P`UsB literal 0 HcmV?d00001 diff --git a/IDA_new/gf-complete/manual/image6.png b/IDA_new/gf-complete/manual/image6.png new file mode 100644 index 0000000000000000000000000000000000000000..33d4cebb321dc949b83f2f355e163930289e2649 GIT binary patch literal 47306 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 literal 0 HcmV?d00001 diff --git a/IDA_new/gf-complete/manual/style.css b/IDA_new/gf-complete/manual/style.css new file mode 100644 index 0000000..3972194 --- /dev/null +++ b/IDA_new/gf-complete/manual/style.css @@ -0,0 +1,404 @@ + +body { +margin:147px 104px 147px 173px; + + +font-size:20px; +text-align:justify; + + + +} + +#index_number{ + +float:right; + +} + +a { + +text-decoration:none; +font-size:19px; +color:#19191F; +letter-spacing:1.5px; + +font-family: 'Roboto Condensed', sans-serif; + + + + + +} +/*This is page1 css */ + +#box { + +text-align:center; +font-size:19px; +margin-top:166px; + + +} + +#body_text{ + +font-family: 'Roboto Condensed', sans-serif; +font-size:18px; + +} + +h1{ +font-weight:inherit; + +} + +h4{ +font-size:22px; + +font-weight:inherit; + +} + +#footer{ + +margin:1px 0px 1px 0px; +font-size:18px; +text-align:justify; +padding-bottom:104px; + + + +} + +p { +margin:0; +text-indent: 50px; +font-size:19px; +text-align:justify; + + +} + + +#footer_bar { +border-top:solid; + +border-top-width:thin; + +} + +#pages_paragraphs { +margin:1px 115px 1px 57px; + + + +} + +#pages_paragraphs_2{ +margin:1px 0px 1px 0px; +font-size:20px; +text-align:justify; +} + +.code{ +font-size:22px; + +} + + + + +/* This is page3 css */ + +.index{ +font-weight:bold; +text-align:justify; + +} + +.sub_indices { + +padding-left:52px; +text-align:justify; +} + + + +.aligning_numbers{ + +padding-left:27px; + + +} + +.aligning_page_number{ + + +float:right; + + +} + +/* This page 6 css */ +.box { + +height:223px; +} + + + +.image-cell_1 { + background: url(image1.png) no-repeat; + width:716px; + height:300px; + + float:left; + margin-left:180px; + margin-right:134px; + margin-bottom:1px; + margin-bottom:31px; + +} + + +/* This page 9 and 10 css */ + + + +#number_spacing{ + +letter-spacing:1px; +font-size:17px; + + +} + + +#number_spacing_1{ + +letter-spacing:1px; +font-size:19px; +margin-left:10px; + + +} + +/* this page 13 css */ + + +.image-cell_2 { + background: url(image2.png) no-repeat; + width:939px; + height:419px; + + float:left; + margin-left:68px; + margin-right:134px; + margin-bottom:1px; + margin-bottom:31px; + +} + +/* This is page 14 */ +#data1 table{ +border-top-style:solid; +border-left-style:solid; + +border-bottom-style:solid; +font-family: 'Roboto Condensed', sans-serif; + +} + +#data1 th{ +border-bottom-style:solid; +border-right-style:solid; +border-right-style:thin; +font-family: 'Roboto Condensed', sans-serif; + + +} + +#data1 td { +border-right-style:solid; + +font-family: 'Roboto Condensed', sans-serif; + +} + + +/* This is page 28 */ +#table_page28 table{ +border-top-style:solid; +border-left-style:solid; + +border-bottom-style:solid; +border-top-width:thin; +border-left-width:thin; +border-bottom-width:thin; +font-family: 'Roboto Condensed', sans-serif; + +} + +#table_page28 th{ +border-bottom-style:solid; +border-right-style:solid; +border-right-width:thin; +border-bottom-width:thin; +font-family: 'Roboto Condensed', sans-serif; + + +} + +#table_page28 td { +border-right-style:solid; +border-bottom-style:solid; +border-bottom-width:thin; +border-right-width:thin; +font-family: 'Roboto Condensed', sans-serif; + +} + + +/* This is page 30 */ +#table_page30 table{ +border-top-style:solid; +border-left-style:solid; + +border-bottom-style:solid; + +} + +#table_page30 th{ +border-bottom-style:solid; +border-right-style:solid; + + +} + +#table_page30 td { +border-right-style:solid; +border-bottom-style:solid; + + +} +#box_1 { + +height:485px; +margin-top:44px; +margin-bottom:-61px; + +} +.image-cell_3 { + background: url(image3.png) no-repeat; + width:583px; + height:393px; + + float:left; + +} + +.image-cell_4 { + background: url(image4.png) no-repeat; + width:487px; + height:390px; + + float:right; + + + +} + +/* This is page 42 Css */ + + +.image-cell_5 { + background: url(image5.png) no-repeat; + width:907px; + height:592px; + + float:left; + margin-right:134px; + margin-bottom:1px; + margin-bottom:31px; + +} + + +/* This is page 43 Css */ + + +.image-cell_6 { + background: url(image6.png) no-repeat; + width:851px; + height:532px; + + margin-right:134px; + margin-bottom:1px; + margin-bottom:31px; + +} + +/* This is page 44 Css */ + + +.image-cell_7{ + background: url(image7.png) no-repeat; + width:945px; + height:321px; + + margin-right:134px; + margin-bottom:1px; + margin-bottom:31px; + +} + +/* This is page 45 */ +#data2 table{ +border-top-style:solid; +border-left-style:solid; + +border-bottom-style:solid; +border-top-width:2px; +border-left-width:2px; +border-bottom-width:2px; +border-color:black; +font-family: 'Roboto Condensed', sans-serif; + +} + +#data2 th{ +border-bottom-style:solid; +border-right-style:solid; +border-bottom-width:2px; +border-right-width:2px; +font-family: 'Roboto Condensed', sans-serif; + + +} + #data2 td { +border-right-style:solid; +border-right-width:2px; +font-family: 'Roboto Condensed', sans-serif; + +} + + + + + + + + + + + + + + + + + + + + diff --git a/IDA_new/gf-complete/src/Makefile.am b/IDA_new/gf-complete/src/Makefile.am new file mode 100644 index 0000000..cfc2a50 --- /dev/null +++ b/IDA_new/gf-complete/src/Makefile.am @@ -0,0 +1,32 @@ +# GF-Complete 'core' AM file +# Creates the library + +AUTOMAKE_OPTIONS = subdir-objects + +AM_CPPFLAGS = -I$(top_builddir)/include -I$(top_srcdir)/include + +# avoid using SIMD_FLAGS for code that calls strcmp as new gcc +# versions will use SIMD for the strcmp implementation. Instead +# we create a static library just for gf_method that is not compiled +# with SIMD_FLAGS, this static library will get linked into gf_complete.so +noinst_LTLIBRARIES = libgf_util.la +libgf_util_la_SOURCES = gf_method.c +libgf_util_la_CFLAGS = -O3 -fPIC -Wsign-compare + +# we narrowly use SIMD_FLAGS for code that needs it +lib_LTLIBRARIES = libgf_complete.la +libgf_complete_la_SOURCES = gf.c gf_wgen.c gf_w4.c gf_w8.c gf_w16.c gf_w32.c \ + gf_w64.c gf_w128.c gf_rand.c gf_general.c gf_cpu.c +libgf_complete_la_CFLAGS = -O3 $(SIMD_FLAGS) -fPIC -Wsign-compare +libgf_complete_la_LIBADD = libgf_util.la + +if HAVE_NEON +libgf_complete_la_SOURCES += neon/gf_w4_neon.c \ + neon/gf_w8_neon.c \ + neon/gf_w16_neon.c \ + neon/gf_w32_neon.c \ + neon/gf_w64_neon.c +endif + +libgf_complete_la_LDFLAGS = -version-info 1:0:0 + diff --git a/IDA_new/gf-complete/src/gf.c b/IDA_new/gf-complete/src/gf.c new file mode 100644 index 0000000..84d6996 --- /dev/null +++ b/IDA_new/gf-complete/src/gf.c @@ -0,0 +1,1090 @@ +/* + * GF-Complete: A Comprehensive Open Source Library for Galois Field Arithmetic + * James S. Plank, Ethan L. Miller, Kevin M. Greenan, + * Benjamin A. Arnold, John A. Burnum, Adam W. Disney, Allen C. McBride. + * + * gf.c + * + * Generic routines for Galois fields + */ + +#include "gf_int.h" +#include +#include +#include +#include "gf_cpu.h" + +int _gf_errno = GF_E_DEFAULT; + +void gf_error() +{ + char *s; + + switch(_gf_errno) { + case GF_E_DEFAULT: s = "No Error."; break; + case GF_E_TWOMULT: s = "Cannot specify two -m's."; break; + case GF_E_TWO_DIV: s = "Cannot specify two -d's."; break; + case GF_E_POLYSPC: s = "-p needs to be followed by a number in hex (0x optional)."; break; + case GF_E_GROUPAR: s = "Ran out of arguments in -m GROUP."; break; + case GF_E_GROUPNU: s = "In -m GROUP g_s g_r -- g_s and g_r need to be numbers."; break; + case GF_E_SPLITAR: s = "Ran out of arguments in -m SPLIT."; break; + case GF_E_SPLITNU: s = "In -m SPLIT w_a w_b -- w_a and w_b need to be numbers."; break; + case GF_E_FEWARGS: s = "Not enough arguments (Perhaps end with '-'?)"; break; + case GF_E_CFM___W: s = "-m CARRY_FREE, w must be 4, 8, 16, 32, 64 or 128."; break; + case GF_E_COMPXPP: s = "-m COMPOSITE, No poly specified, and we don't have a default for the given sub-field."; break; + case GF_E_BASE__W: s = "-m COMPOSITE and the base field is not for w/2."; break; + case GF_E_CFM4POL: s = "-m CARRY_FREE, w=4. (Prim-poly & 0xc) must equal 0."; break; + case GF_E_CFM8POL: s = "-m CARRY_FREE, w=8. (Prim-poly & 0x80) must equal 0."; break; + case GF_E_CF16POL: s = "-m CARRY_FREE, w=16. (Prim-poly & 0xe000) must equal 0."; break; + case GF_E_CF32POL: s = "-m CARRY_FREE, w=32. (Prim-poly & 0xfe000000) must equal 0."; break; + case GF_E_CF64POL: s = "-m CARRY_FREE, w=64. (Prim-poly & 0xfffe000000000000ULL) must equal 0."; break; + case GF_E_MDEFDIV: s = "If multiplication method == default, can't change division."; break; + case GF_E_MDEFREG: s = "If multiplication method == default, can't change region."; break; + case GF_E_MDEFARG: s = "If multiplication method == default, can't use arg1/arg2."; break; + case GF_E_DIVCOMP: s = "Cannot change the division technique with -m COMPOSITE."; break; + case GF_E_DOUQUAD: s = "Cannot specify -r DOUBLE and -r QUAD."; break; + case GF_E_SIMD_NO: s = "Cannot specify -r SIMD and -r NOSIMD."; break; + case GF_E_CAUCHYB: s = "Cannot specify -r CAUCHY and any other -r."; break; + case GF_E_CAUCOMP: s = "Cannot specify -m COMPOSITE and -r CAUCHY."; break; + case GF_E_CAUGT32: s = "Cannot specify -r CAUCHY with w > 32."; break; + case GF_E_ARG1SET: s = "Only use arg1 with SPLIT, GROUP or COMPOSITE."; break; + case GF_E_ARG2SET: s = "Only use arg2 with SPLIT or GROUP."; break; + case GF_E_MATRIXW: s = "Cannot specify -d MATRIX with w > 32."; break; + case GF_E_BAD___W: s = "W must be 1-32, 64 or 128."; break; + case GF_E_DOUBLET: s = "Can only specify -r DOUBLE with -m TABLE."; break; + case GF_E_DOUBLEW: s = "Can only specify -r DOUBLE w = 4 or w = 8."; break; + case GF_E_DOUBLEJ: s = "Cannot specify -r DOUBLE with -r ALTMAP|SIMD|NOSIMD."; break; + case GF_E_DOUBLEL: s = "Can only specify -r DOUBLE -r LAZY with w = 8"; break; + case GF_E_QUAD__T: s = "Can only specify -r QUAD with -m TABLE."; break; + case GF_E_QUAD__W: s = "Can only specify -r QUAD w = 4."; break; + case GF_E_QUAD__J: s = "Cannot specify -r QUAD with -r ALTMAP|SIMD|NOSIMD."; break; + case GF_E_BADPOLY: s = "Bad primitive polynomial (high bits set)."; break; + case GF_E_COMP_PP: s = "Bad primitive polynomial -- bigger than sub-field."; break; + case GF_E_LAZY__X: s = "If -r LAZY, then -r must be DOUBLE or QUAD."; break; + case GF_E_ALTSHIF: s = "Cannot specify -m SHIFT and -r ALTMAP."; break; + case GF_E_SSESHIF: s = "Cannot specify -m SHIFT and -r SIMD|NOSIMD."; break; + case GF_E_ALT_CFM: s = "Cannot specify -m CARRY_FREE and -r ALTMAP."; break; + case GF_E_SSE_CFM: s = "Cannot specify -m CARRY_FREE and -r SIMD|NOSIMD."; break; + case GF_E_PCLMULX: s = "Specified -m CARRY_FREE, but PCLMUL is not supported."; break; + case GF_E_ALT_BY2: s = "Cannot specify -m BYTWO_x and -r ALTMAP."; break; + case GF_E_BY2_SSE: s = "Specified -m BYTWO_x -r SIMD, but SSE2 is not supported."; break; + case GF_E_LOGBADW: s = "With Log Tables, w must be <= 27."; break; + case GF_E_LOG___J: s = "Cannot use Log tables with -r ALTMAP|SIMD|NOSIMD."; break; + case GF_E_LOGPOLY: s = "Cannot use Log tables because the polynomial is not primitive."; break; + case GF_E_ZERBADW: s = "With -m LOG_ZERO, w must be 8 or 16."; break; + case GF_E_ZEXBADW: s = "With -m LOG_ZERO_EXT, w must be 8."; break; + case GF_E_GR_ARGX: s = "With -m GROUP, arg1 and arg2 must be >= 0."; break; + case GF_E_GR_W_48: s = "With -m GROUP, w cannot be 4 or 8."; break; + case GF_E_GR_W_16: s = "With -m GROUP, w == 16, arg1 and arg2 must be 4."; break; + case GF_E_GR_128A: s = "With -m GROUP, w == 128, arg1 must be 4, and arg2 in { 4,8,16 }."; break; + case GF_E_GR_A_27: s = "With -m GROUP, arg1 and arg2 must be <= 27."; break; + case GF_E_GR_AR_W: s = "With -m GROUP, arg1 and arg2 must be <= w."; break; + case GF_E_GR____J: s = "Cannot use GROUP with -r ALTMAP|SIMD|NOSIMD."; break; + case GF_E_TABLE_W: s = "With -m TABLE, w must be < 15, or == 16."; break; + case GF_E_TAB_SSE: s = "With -m TABLE, SIMD|NOSIMD only applies to w=4."; break; + case GF_E_TABSSE3: s = "With -m TABLE, -r SIMD, you need SSSE3 supported."; break; + case GF_E_TAB_ALT: s = "With -m TABLE, you cannot use ALTMAP."; break; + case GF_E_SP128AR: s = "With -m SPLIT, w=128, bad arg1/arg2."; break; + case GF_E_SP128AL: s = "With -m SPLIT, w=128, -r SIMD requires -r ALTMAP."; break; + case GF_E_SP128AS: s = "With -m SPLIT, w=128, ALTMAP needs SSSE3 supported."; break; + case GF_E_SP128_A: s = "With -m SPLIT, w=128, -r ALTMAP only with arg1/arg2 = 4/128."; break; + case GF_E_SP128_S: s = "With -m SPLIT, w=128, -r SIMD|NOSIMD only with arg1/arg2 = 4/128."; break; + case GF_E_SPLIT_W: s = "With -m SPLIT, w must be in {8, 16, 32, 64, 128}."; break; + case GF_E_SP_16AR: s = "With -m SPLIT, w=16, Bad arg1/arg2."; break; + case GF_E_SP_16_A: s = "With -m SPLIT, w=16, -r ALTMAP only with arg1/arg2 = 4/16."; break; + case GF_E_SP_16_S: s = "With -m SPLIT, w=16, -r SIMD|NOSIMD only with arg1/arg2 = 4/16."; break; + case GF_E_SP_32AR: s = "With -m SPLIT, w=32, Bad arg1/arg2."; break; + case GF_E_SP_32AS: s = "With -m SPLIT, w=32, -r ALTMAP needs SSSE3 supported."; break; + case GF_E_SP_32_A: s = "With -m SPLIT, w=32, -r ALTMAP only with arg1/arg2 = 4/32."; break; + case GF_E_SP_32_S: s = "With -m SPLIT, w=32, -r SIMD|NOSIMD only with arg1/arg2 = 4/32."; break; + case GF_E_SP_64AR: s = "With -m SPLIT, w=64, Bad arg1/arg2."; break; + case GF_E_SP_64AS: s = "With -m SPLIT, w=64, -r ALTMAP needs SSSE3 supported."; break; + case GF_E_SP_64_A: s = "With -m SPLIT, w=64, -r ALTMAP only with arg1/arg2 = 4/64."; break; + case GF_E_SP_64_S: s = "With -m SPLIT, w=64, -r SIMD|NOSIMD only with arg1/arg2 = 4/64."; break; + case GF_E_SP_8_AR: s = "With -m SPLIT, w=8, Bad arg1/arg2."; break; + case GF_E_SP_8__A: s = "With -m SPLIT, w=8, Can't have -r ALTMAP."; break; + case GF_E_SP_SSE3: s = "With -m SPLIT, Need SSSE3 support for SIMD."; break; + case GF_E_COMP_A2: s = "With -m COMPOSITE, arg1 must equal 2."; break; + case GF_E_COMP_SS: s = "With -m COMPOSITE, -r SIMD and -r NOSIMD do not apply."; break; + case GF_E_COMP__W: s = "With -m COMPOSITE, w must be 8, 16, 32, 64 or 128."; break; + case GF_E_UNKFLAG: s = "Unknown method flag - should be -m, -d, -r or -p."; break; + case GF_E_UNKNOWN: s = "Unknown multiplication type."; break; + case GF_E_UNK_REG: s = "Unknown region type."; break; + case GF_E_UNK_DIV: s = "Unknown division type."; break; + default: s = "Undefined error."; + } + + fprintf(stderr, "%s\n", s); +} + +uint64_t gf_composite_get_default_poly(gf_t *base) +{ + gf_internal_t *h; + uint64_t rv; + + h = (gf_internal_t *) base->scratch; + if (h->w == 4) { + if (h->mult_type == GF_MULT_COMPOSITE) return 0; + if (h->prim_poly == 0x13) return 2; + return 0; + } + if (h->w == 8) { + if (h->mult_type == GF_MULT_COMPOSITE) return 0; + if (h->prim_poly == 0x11d) return 3; + return 0; + } + if (h->w == 16) { + if (h->mult_type == GF_MULT_COMPOSITE) { + rv = gf_composite_get_default_poly(h->base_gf); + if (rv != h->prim_poly) return 0; + if (rv == 3) return 0x105; + return 0; + } else { + if (h->prim_poly == 0x1100b) return 2; + if (h->prim_poly == 0x1002d) return 7; + return 0; + } + } + if (h->w == 32) { + if (h->mult_type == GF_MULT_COMPOSITE) { + rv = gf_composite_get_default_poly(h->base_gf); + if (rv != h->prim_poly) return 0; + if (rv == 2) return 0x10005; + if (rv == 7) return 0x10008; + if (rv == 0x105) return 0x10002; + return 0; + } else { + if (h->prim_poly == 0x400007) return 2; + if (h->prim_poly == 0xc5) return 3; + return 0; + } + } + if (h->w == 64) { + if (h->mult_type == GF_MULT_COMPOSITE) { + rv = gf_composite_get_default_poly(h->base_gf); + if (rv != h->prim_poly) return 0; + if (rv == 3) return 0x100000009ULL; + if (rv == 2) return 0x100000004ULL; + if (rv == 0x10005) return 0x100000003ULL; + if (rv == 0x10002) return 0x100000005ULL; + if (rv == 0x10008) return 0x100000006ULL; /* JSP: (0x0x100000003 works too, + but I want to differentiate cases). */ + return 0; + } else { + if (h->prim_poly == 0x1bULL) return 2; + return 0; + } + } + return 0; +} + +int gf_error_check(int w, int mult_type, int region_type, int divide_type, + int arg1, int arg2, uint64_t poly, gf_t *base) +{ + int sse3 = 0; + int sse2 = 0; + int pclmul = 0; + int rdouble, rquad, rlazy, rsimd, rnosimd, raltmap, rcauchy, tmp; + gf_internal_t *sub; + + rdouble = (region_type & GF_REGION_DOUBLE_TABLE); + rquad = (region_type & GF_REGION_QUAD_TABLE); + rlazy = (region_type & GF_REGION_LAZY); + rsimd = (region_type & GF_REGION_SIMD); + rnosimd = (region_type & GF_REGION_NOSIMD); + raltmap = (region_type & GF_REGION_ALTMAP); + rcauchy = (region_type & GF_REGION_CAUCHY); + + if (divide_type != GF_DIVIDE_DEFAULT && + divide_type != GF_DIVIDE_MATRIX && + divide_type != GF_DIVIDE_EUCLID) { + _gf_errno = GF_E_UNK_DIV; + return 0; + } + + tmp = ( GF_REGION_DOUBLE_TABLE | GF_REGION_QUAD_TABLE | GF_REGION_LAZY | + GF_REGION_SIMD | GF_REGION_NOSIMD | GF_REGION_ALTMAP | + GF_REGION_CAUCHY ); + if (region_type & (~tmp)) { _gf_errno = GF_E_UNK_REG; return 0; } + +#ifdef INTEL_SSE2 + if (gf_cpu_supports_intel_sse2) { + sse2 = 1; + } +#endif + +#ifdef INTEL_SSSE3 + if (gf_cpu_supports_intel_ssse3) { + sse3 = 1; + } +#endif + +#ifdef INTEL_SSE4_PCLMUL + if (gf_cpu_supports_intel_pclmul) { + pclmul = 1; + } +#endif + +#ifdef ARM_NEON + if (gf_cpu_supports_arm_neon) { + pclmul = (w == 4 || w == 8); + sse3 = 1; + } +#endif + + + if (w < 1 || (w > 32 && w != 64 && w != 128)) { _gf_errno = GF_E_BAD___W; return 0; } + + if (mult_type != GF_MULT_COMPOSITE && w < 64) { + if ((poly >> (w+1)) != 0) { _gf_errno = GF_E_BADPOLY; return 0; } + } + + if (mult_type == GF_MULT_DEFAULT) { + if (divide_type != GF_DIVIDE_DEFAULT) { _gf_errno = GF_E_MDEFDIV; return 0; } + if (region_type != GF_REGION_DEFAULT) { _gf_errno = GF_E_MDEFREG; return 0; } + if (arg1 != 0 || arg2 != 0) { _gf_errno = GF_E_MDEFARG; return 0; } + return 1; + } + + if (rsimd && rnosimd) { _gf_errno = GF_E_SIMD_NO; return 0; } + if (rcauchy && w > 32) { _gf_errno = GF_E_CAUGT32; return 0; } + if (rcauchy && region_type != GF_REGION_CAUCHY) { _gf_errno = GF_E_CAUCHYB; return 0; } + if (rcauchy && mult_type == GF_MULT_COMPOSITE) { _gf_errno = GF_E_CAUCOMP; return 0; } + + if (arg1 != 0 && mult_type != GF_MULT_COMPOSITE && + mult_type != GF_MULT_SPLIT_TABLE && mult_type != GF_MULT_GROUP) { + _gf_errno = GF_E_ARG1SET; + return 0; + } + + if (arg2 != 0 && mult_type != GF_MULT_SPLIT_TABLE && mult_type != GF_MULT_GROUP) { + _gf_errno = GF_E_ARG2SET; + return 0; + } + + if (divide_type == GF_DIVIDE_MATRIX && w > 32) { _gf_errno = GF_E_MATRIXW; return 0; } + + if (rdouble) { + if (rquad) { _gf_errno = GF_E_DOUQUAD; return 0; } + if (mult_type != GF_MULT_TABLE) { _gf_errno = GF_E_DOUBLET; return 0; } + if (w != 4 && w != 8) { _gf_errno = GF_E_DOUBLEW; return 0; } + if (rsimd || rnosimd || raltmap) { _gf_errno = GF_E_DOUBLEJ; return 0; } + if (rlazy && w == 4) { _gf_errno = GF_E_DOUBLEL; return 0; } + return 1; + } + + if (rquad) { + if (mult_type != GF_MULT_TABLE) { _gf_errno = GF_E_QUAD__T; return 0; } + if (w != 4) { _gf_errno = GF_E_QUAD__W; return 0; } + if (rsimd || rnosimd || raltmap) { _gf_errno = GF_E_QUAD__J; return 0; } + return 1; + } + + if (rlazy) { _gf_errno = GF_E_LAZY__X; return 0; } + + if (mult_type == GF_MULT_SHIFT) { + if (raltmap) { _gf_errno = GF_E_ALTSHIF; return 0; } + if (rsimd || rnosimd) { _gf_errno = GF_E_SSESHIF; return 0; } + return 1; + } + + if (mult_type == GF_MULT_CARRY_FREE) { + if (w != 4 && w != 8 && w != 16 && + w != 32 && w != 64 && w != 128) { _gf_errno = GF_E_CFM___W; return 0; } + if (w == 4 && (poly & 0xc)) { _gf_errno = GF_E_CFM4POL; return 0; } + if (w == 8 && (poly & 0x80)) { _gf_errno = GF_E_CFM8POL; return 0; } + if (w == 16 && (poly & 0xe000)) { _gf_errno = GF_E_CF16POL; return 0; } + if (w == 32 && (poly & 0xfe000000)) { _gf_errno = GF_E_CF32POL; return 0; } + if (w == 64 && (poly & 0xfffe000000000000ULL)) { _gf_errno = GF_E_CF64POL; return 0; } + if (raltmap) { _gf_errno = GF_E_ALT_CFM; return 0; } + if (rsimd || rnosimd) { _gf_errno = GF_E_SSE_CFM; return 0; } + if (!pclmul) { _gf_errno = GF_E_PCLMULX; return 0; } + return 1; + } + + if (mult_type == GF_MULT_CARRY_FREE_GK) { + if (w != 4 && w != 8 && w != 16 && + w != 32 && w != 64 && w != 128) { _gf_errno = GF_E_CFM___W; return 0; } + if (raltmap) { _gf_errno = GF_E_ALT_CFM; return 0; } + if (rsimd || rnosimd) { _gf_errno = GF_E_SSE_CFM; return 0; } + if (!pclmul) { _gf_errno = GF_E_PCLMULX; return 0; } + return 1; + } + + if (mult_type == GF_MULT_BYTWO_p || mult_type == GF_MULT_BYTWO_b) { + if (raltmap) { _gf_errno = GF_E_ALT_BY2; return 0; } + if (rsimd && !sse2) { _gf_errno = GF_E_BY2_SSE; return 0; } + return 1; + } + + if (mult_type == GF_MULT_LOG_TABLE || mult_type == GF_MULT_LOG_ZERO + || mult_type == GF_MULT_LOG_ZERO_EXT ) { + if (w > 27) { _gf_errno = GF_E_LOGBADW; return 0; } + if (raltmap || rsimd || rnosimd) { _gf_errno = GF_E_LOG___J; return 0; } + + if (mult_type == GF_MULT_LOG_TABLE) return 1; + + if (w != 8 && w != 16) { _gf_errno = GF_E_ZERBADW; return 0; } + + if (mult_type == GF_MULT_LOG_ZERO) return 1; + + if (w != 8) { _gf_errno = GF_E_ZEXBADW; return 0; } + return 1; + } + + if (mult_type == GF_MULT_GROUP) { + if (arg1 <= 0 || arg2 <= 0) { _gf_errno = GF_E_GR_ARGX; return 0; } + if (w == 4 || w == 8) { _gf_errno = GF_E_GR_W_48; return 0; } + if (w == 16 && (arg1 != 4 || arg2 != 4)) { _gf_errno = GF_E_GR_W_16; return 0; } + if (w == 128 && (arg1 != 4 || + (arg2 != 4 && arg2 != 8 && arg2 != 16))) { _gf_errno = GF_E_GR_128A; return 0; } + if (arg1 > 27 || arg2 > 27) { _gf_errno = GF_E_GR_A_27; return 0; } + if (arg1 > w || arg2 > w) { _gf_errno = GF_E_GR_AR_W; return 0; } + if (raltmap || rsimd || rnosimd) { _gf_errno = GF_E_GR____J; return 0; } + return 1; + } + + if (mult_type == GF_MULT_TABLE) { + if (w != 16 && w >= 15) { _gf_errno = GF_E_TABLE_W; return 0; } + if (w != 4 && (rsimd || rnosimd)) { _gf_errno = GF_E_TAB_SSE; return 0; } + if (rsimd && !sse3) { _gf_errno = GF_E_TABSSE3; return 0; } + if (raltmap) { _gf_errno = GF_E_TAB_ALT; return 0; } + return 1; + } + + if (mult_type == GF_MULT_SPLIT_TABLE) { + if (arg1 > arg2) { + tmp = arg1; + arg1 = arg2; + arg2 = tmp; + } + if (w == 8) { + if (arg1 != 4 || arg2 != 8) { _gf_errno = GF_E_SP_8_AR; return 0; } + if (rsimd && !sse3) { _gf_errno = GF_E_SP_SSE3; return 0; } + if (raltmap) { _gf_errno = GF_E_SP_8__A; return 0; } + } else if (w == 16) { + if ((arg1 == 8 && arg2 == 8) || + (arg1 == 8 && arg2 == 16)) { + if (rsimd || rnosimd) { _gf_errno = GF_E_SP_16_S; return 0; } + if (raltmap) { _gf_errno = GF_E_SP_16_A; return 0; } + } else if (arg1 == 4 && arg2 == 16) { + if (rsimd && !sse3) { _gf_errno = GF_E_SP_SSE3; return 0; } + } else { _gf_errno = GF_E_SP_16AR; return 0; } + } else if (w == 32) { + if ((arg1 == 8 && arg2 == 8) || + (arg1 == 8 && arg2 == 32) || + (arg1 == 16 && arg2 == 32)) { + if (rsimd || rnosimd) { _gf_errno = GF_E_SP_32_S; return 0; } + if (raltmap) { _gf_errno = GF_E_SP_32_A; return 0; } + } else if (arg1 == 4 && arg2 == 32) { + if (rsimd && !sse3) { _gf_errno = GF_E_SP_SSE3; return 0; } + if (raltmap && !sse3) { _gf_errno = GF_E_SP_32AS; return 0; } + if (raltmap && rnosimd) { _gf_errno = GF_E_SP_32AS; return 0; } + } else { _gf_errno = GF_E_SP_32AR; return 0; } + } else if (w == 64) { + if ((arg1 == 8 && arg2 == 8) || + (arg1 == 8 && arg2 == 64) || + (arg1 == 16 && arg2 == 64)) { + if (rsimd || rnosimd) { _gf_errno = GF_E_SP_64_S; return 0; } + if (raltmap) { _gf_errno = GF_E_SP_64_A; return 0; } + } else if (arg1 == 4 && arg2 == 64) { + if (rsimd && !sse3) { _gf_errno = GF_E_SP_SSE3; return 0; } + if (raltmap && !sse3) { _gf_errno = GF_E_SP_64AS; return 0; } + if (raltmap && rnosimd) { _gf_errno = GF_E_SP_64AS; return 0; } + } else { _gf_errno = GF_E_SP_64AR; return 0; } + } else if (w == 128) { + if (arg1 == 8 && arg2 == 128) { + if (rsimd || rnosimd) { _gf_errno = GF_E_SP128_S; return 0; } + if (raltmap) { _gf_errno = GF_E_SP128_A; return 0; } + } else if (arg1 == 4 && arg2 == 128) { + if (rsimd && !sse3) { _gf_errno = GF_E_SP_SSE3; return 0; } + if (raltmap && !sse3) { _gf_errno = GF_E_SP128AS; return 0; } + if (raltmap && rnosimd) { _gf_errno = GF_E_SP128AS; return 0; } + } else { _gf_errno = GF_E_SP128AR; return 0; } + } else { _gf_errno = GF_E_SPLIT_W; return 0; } + return 1; + } + + if (mult_type == GF_MULT_COMPOSITE) { + if (w != 8 && w != 16 && w != 32 + && w != 64 && w != 128) { _gf_errno = GF_E_COMP__W; return 0; } + if (w < 128 && (poly >> (w/2)) != 0) { _gf_errno = GF_E_COMP_PP; return 0; } + if (divide_type != GF_DIVIDE_DEFAULT) { _gf_errno = GF_E_DIVCOMP; return 0; } + if (arg1 != 2) { _gf_errno = GF_E_COMP_A2; return 0; } + if (rsimd || rnosimd) { _gf_errno = GF_E_COMP_SS; return 0; } + if (base != NULL) { + sub = (gf_internal_t *) base->scratch; + if (sub->w != w/2) { _gf_errno = GF_E_BASE__W; return 0; } + if (poly == 0) { + if (gf_composite_get_default_poly(base) == 0) { _gf_errno = GF_E_COMPXPP; return 0; } + } + } + return 1; + } + + _gf_errno = GF_E_UNKNOWN; + return 0; +} + +int gf_scratch_size(int w, + int mult_type, + int region_type, + int divide_type, + int arg1, + int arg2) +{ + if (gf_error_check(w, mult_type, region_type, divide_type, arg1, arg2, 0, NULL) == 0) return 0; + + switch(w) { + case 4: return gf_w4_scratch_size(mult_type, region_type, divide_type, arg1, arg2); + case 8: return gf_w8_scratch_size(mult_type, region_type, divide_type, arg1, arg2); + case 16: return gf_w16_scratch_size(mult_type, region_type, divide_type, arg1, arg2); + case 32: return gf_w32_scratch_size(mult_type, region_type, divide_type, arg1, arg2); + case 64: return gf_w64_scratch_size(mult_type, region_type, divide_type, arg1, arg2); + case 128: return gf_w128_scratch_size(mult_type, region_type, divide_type, arg1, arg2); + default: return gf_wgen_scratch_size(w, mult_type, region_type, divide_type, arg1, arg2); + } +} + +extern int gf_size(gf_t *gf) +{ + gf_internal_t *h; + int s; + + s = sizeof(gf_t); + h = (gf_internal_t *) gf->scratch; + s += gf_scratch_size(h->w, h->mult_type, h->region_type, h->divide_type, h->arg1, h->arg2); + if (h->mult_type == GF_MULT_COMPOSITE) s += gf_size(h->base_gf); + return s; +} + + +int gf_init_easy(gf_t *gf, int w) +{ + return gf_init_hard(gf, w, GF_MULT_DEFAULT, GF_REGION_DEFAULT, GF_DIVIDE_DEFAULT, + 0, 0, 0, NULL, NULL); +} + +/* Allen: What's going on here is this function is putting info into the + scratch mem of gf, and then calling the relevant REAL init + func for the word size. Probably done this way to consolidate + those aspects of initialization that don't rely on word size, + and then take care of word-size-specific stuff. */ + +int gf_init_hard(gf_t *gf, int w, int mult_type, + int region_type, + int divide_type, + uint64_t prim_poly, + int arg1, int arg2, + gf_t *base_gf, + void *scratch_memory) +{ + int sz; + gf_internal_t *h; + + gf_cpu_identify(); + + if (gf_error_check(w, mult_type, region_type, divide_type, + arg1, arg2, prim_poly, base_gf) == 0) return 0; + + sz = gf_scratch_size(w, mult_type, region_type, divide_type, arg1, arg2); + if (sz <= 0) return 0; /* This shouldn't happen, as all errors should get caught + in gf_error_check() */ + + if (scratch_memory == NULL) { + h = (gf_internal_t *) malloc(sz); + h->free_me = 1; + } else { + h = scratch_memory; + h->free_me = 0; + } + gf->scratch = (void *) h; + h->mult_type = mult_type; + h->region_type = region_type; + h->divide_type = divide_type; + h->w = w; + h->prim_poly = prim_poly; + h->arg1 = arg1; + h->arg2 = arg2; + h->base_gf = base_gf; + h->private = (void *) gf->scratch; + h->private = (uint8_t *)h->private + (sizeof(gf_internal_t)); + gf->extract_word.w32 = NULL; + + switch(w) { + case 4: return gf_w4_init(gf); + case 8: return gf_w8_init(gf); + case 16: return gf_w16_init(gf); + case 32: return gf_w32_init(gf); + case 64: return gf_w64_init(gf); + case 128: return gf_w128_init(gf); + default: return gf_wgen_init(gf); + } +} + +int gf_free(gf_t *gf, int recursive) +{ + gf_internal_t *h; + + h = (gf_internal_t *) gf->scratch; + if (recursive && h->base_gf != NULL) { + gf_free(h->base_gf, 1); + free(h->base_gf); + } + if (h->free_me) free(h); + return 0; /* Making compiler happy */ +} + +void gf_alignment_error(char *s, int a) +{ + fprintf(stderr, "Alignment error in %s:\n", s); + fprintf(stderr, " The source and destination buffers must be aligned to each other,\n"); + fprintf(stderr, " and they must be aligned to a %d-byte address.\n", a); + assert(0); +} + +static +void gf_invert_binary_matrix(uint32_t *mat, uint32_t *inv, int rows) { + int cols, i, j; + uint32_t tmp; + + cols = rows; + + for (i = 0; i < rows; i++) inv[i] = (1 << i); + + /* First -- convert into upper triangular */ + + for (i = 0; i < cols; i++) { + + /* Swap rows if we ave a zero i,i element. If we can't swap, then the + matrix was not invertible */ + + if ((mat[i] & (1 << i)) == 0) { + for (j = i+1; j < rows && (mat[j] & (1 << i)) == 0; j++) ; + if (j == rows) { + fprintf(stderr, "galois_invert_matrix: Matrix not invertible!!\n"); + assert(0); + } + tmp = mat[i]; mat[i] = mat[j]; mat[j] = tmp; + tmp = inv[i]; inv[i] = inv[j]; inv[j] = tmp; + } + + /* Now for each j>i, add A_ji*Ai to Aj */ + for (j = i+1; j != rows; j++) { + if ((mat[j] & (1 << i)) != 0) { + mat[j] ^= mat[i]; + inv[j] ^= inv[i]; + } + } + } + + /* Now the matrix is upper triangular. Start at the top and multiply down */ + + for (i = rows-1; i >= 0; i--) { + for (j = 0; j < i; j++) { + if (mat[j] & (1 << i)) { + /* mat[j] ^= mat[i]; */ + inv[j] ^= inv[i]; + } + } + } +} + +uint32_t gf_bitmatrix_inverse(uint32_t y, int w, uint32_t pp) +{ + uint32_t mat[32], inv[32], mask; + int i; + + mask = (w == 32) ? 0xffffffff : ((uint32_t)1 << w) - 1; + for (i = 0; i < w; i++) { + mat[i] = y; + + if (y & (1 << (w-1))) { + y = y << 1; + y = ((y ^ pp) & mask); + } else { + y = y << 1; + } + } + + gf_invert_binary_matrix(mat, inv, w); + return inv[0]; +} + +void gf_two_byte_region_table_multiply(gf_region_data *rd, uint16_t *base) +{ + uint64_t a, prod; + int xor; + uint64_t *s64, *d64, *top; + + s64 = rd->s_start; + d64 = rd->d_start; + top = rd->d_top; + xor = rd->xor; + + if (xor) { + while (d64 != top) { + a = *s64; + prod = base[a >> 48]; + a <<= 16; + prod <<= 16; + prod ^= base[a >> 48]; + a <<= 16; + prod <<= 16; + prod ^= base[a >> 48]; + a <<= 16; + prod <<= 16; + prod ^= base[a >> 48]; + prod ^= *d64; + *d64 = prod; + s64++; + d64++; + } + } else { + while (d64 != top) { + a = *s64; + prod = base[a >> 48]; + a <<= 16; + prod <<= 16; + prod ^= base[a >> 48]; + a <<= 16; + prod <<= 16; + prod ^= base[a >> 48]; + a <<= 16; + prod <<= 16; + prod ^= base[a >> 48]; + *d64 = prod; + s64++; + d64++; + } + } +} + +static void gf_slow_multiply_region(gf_region_data *rd, void *src, void *dest, void *s_top) +{ + uint8_t *s8, *d8; + uint16_t *s16, *d16; + uint32_t *s32, *d32; + uint64_t *s64, *d64; + gf_internal_t *h; + int wb; + uint32_t p, a; + + h = rd->gf->scratch; + wb = (h->w)/8; + if (wb == 0) wb = 1; + + while (src < s_top) { + switch (h->w) { + case 8: + s8 = (uint8_t *) src; + d8 = (uint8_t *) dest; + *d8 = (rd->xor) ? (*d8 ^ rd->gf->multiply.w32(rd->gf, rd->val, *s8)) : + rd->gf->multiply.w32(rd->gf, rd->val, *s8); + break; + case 4: + s8 = (uint8_t *) src; + d8 = (uint8_t *) dest; + a = *s8; + p = rd->gf->multiply.w32(rd->gf, rd->val, a&0xf); + p |= (rd->gf->multiply.w32(rd->gf, rd->val, a >> 4) << 4); + if (rd->xor) p ^= *d8; + *d8 = p; + break; + case 16: + s16 = (uint16_t *) src; + d16 = (uint16_t *) dest; + *d16 = (rd->xor) ? (*d16 ^ rd->gf->multiply.w32(rd->gf, rd->val, *s16)) : + rd->gf->multiply.w32(rd->gf, rd->val, *s16); + break; + case 32: + s32 = (uint32_t *) src; + d32 = (uint32_t *) dest; + *d32 = (rd->xor) ? (*d32 ^ rd->gf->multiply.w32(rd->gf, rd->val, *s32)) : + rd->gf->multiply.w32(rd->gf, rd->val, *s32); + break; + case 64: + s64 = (uint64_t *) src; + d64 = (uint64_t *) dest; + *d64 = (rd->xor) ? (*d64 ^ rd->gf->multiply.w64(rd->gf, rd->val, *s64)) : + rd->gf->multiply.w64(rd->gf, rd->val, *s64); + break; + default: + fprintf(stderr, "Error: gf_slow_multiply_region: w=%d not implemented.\n", h->w); + exit(1); + } + src = (uint8_t *)src + wb; + dest = (uint8_t *)dest + wb; + } +} + +/* JSP - The purpose of this procedure is to error check alignment, + and to set up the region operation so that it can best leverage + large words. + + It stores its information in rd. + + Assuming you're not doing Cauchy coding, (see below for that), + then w will be 4, 8, 16, 32 or 64. It can't be 128 (probably + should change that). + + src and dest must then be aligned on ceil(w/8)-byte boundaries. + Moreover, bytes must be a multiple of ceil(w/8). If the variable + align is equal to ceil(w/8), then we will set s_start = src, + d_start = dest, s_top to (src+bytes) and d_top to (dest+bytes). + And we return -- the implementation will go ahead and do the + multiplication on individual words (e.g. using discrete logs). + + If align is greater than ceil(w/8), then the implementation needs + to work on groups of "align" bytes. For example, suppose you are + implementing BYTWO, without SSE. Then you will be doing the region + multiplication in units of 8 bytes, so align = 8. Or, suppose you + are doing a Quad table in GF(2^4). You will be doing the region + multiplication in units of 2 bytes, so align = 2. Or, suppose you + are doing split multiplication with SSE operations in GF(2^8). + Then align = 16. Worse yet, suppose you are doing split + multiplication with SSE operations in GF(2^16), with or without + ALTMAP. Then, you will be doing the multiplication on 256 bits at + a time. So align = 32. + + When align does not equal ceil(w/8), we split the region + multiplication into three parts. We are going to make s_start be + the first address greater than or equal to src that is a multiple + of align. s_top is going to be the largest address >= src+bytes + such that (s_top - s_start) is a multiple of align. We do the + same with d_start and d_top. When we say that "src and dest must + be aligned with respect to each other, we mean that s_start-src + must equal d_start-dest. + + Now, the region multiplication is done in three parts -- the part + between src and s_start must be done using single words. + Similarly, the part between s_top and src+bytes must also be done + using single words. The part between s_start and s_top will be + done in chunks of "align" bytes. + + One final thing -- if align > 16, then s_start and d_start will be + aligned on a 16 byte boundary. Perhaps we should have two + variables: align and chunksize. Then we'd have s_start & d_start + aligned to "align", and have s_top-s_start be a multiple of + chunksize. That may be less confusing, but it would be a big + change. + + Finally, if align = -1, then we are doing Cauchy multiplication, + using only XOR's. In this case, we're not going to care about + alignment because we are just doing XOR's. Instead, the only + thing we care about is that bytes must be a multiple of w. + + This is not to say that alignment doesn't matter in performance + with XOR's. See that discussion in gf_multby_one(). + + After you call gf_set_region_data(), the procedure + gf_do_initial_region_alignment() calls gf->multiply.w32() on + everything between src and s_start. The procedure + gf_do_final_region_alignment() calls gf->multiply.w32() on + everything between s_top and src+bytes. + */ + +void gf_set_region_data(gf_region_data *rd, + gf_t *gf, + void *src, + void *dest, + int bytes, + uint64_t val, + int xor, + int align) +{ + gf_internal_t *h = NULL; + int wb; + uint32_t a; + unsigned long uls, uld; + + if (gf == NULL) { /* JSP - Can be NULL if you're just doing XOR's */ + wb = 1; + } else { + h = gf->scratch; + wb = (h->w)/8; + if (wb == 0) wb = 1; + } + + rd->gf = gf; + rd->src = src; + rd->dest = dest; + rd->bytes = bytes; + rd->val = val; + rd->xor = xor; + rd->align = align; + + uls = (unsigned long) src; + uld = (unsigned long) dest; + + a = (align <= 16) ? align : 16; + + if (align == -1) { /* JSP: This is cauchy. Error check bytes, then set up the pointers + so that there are no alignment regions. */ + if (h != NULL && bytes % h->w != 0) { + fprintf(stderr, "Error in region multiply operation.\n"); + fprintf(stderr, "The size must be a multiple of %d bytes.\n", h->w); + assert(0); + } + + rd->s_start = src; + rd->d_start = dest; + rd->s_top = (uint8_t *)src + bytes; + rd->d_top = (uint8_t *)src + bytes; + return; + } + + if (uls % a != uld % a) { + fprintf(stderr, "Error in region multiply operation.\n"); + fprintf(stderr, "The source & destination pointers must be aligned with respect\n"); + fprintf(stderr, "to each other along a %d byte boundary.\n", a); + fprintf(stderr, "Src = 0x%lx. Dest = 0x%lx\n", (unsigned long) src, + (unsigned long) dest); + assert(0); + } + + if (uls % wb != 0) { + fprintf(stderr, "Error in region multiply operation.\n"); + fprintf(stderr, "The pointers must be aligned along a %d byte boundary.\n", wb); + fprintf(stderr, "Src = 0x%lx. Dest = 0x%lx\n", (unsigned long) src, + (unsigned long) dest); + assert(0); + } + + if (bytes % wb != 0) { + fprintf(stderr, "Error in region multiply operation.\n"); + fprintf(stderr, "The size must be a multiple of %d bytes.\n", wb); + assert(0); + } + + uls %= a; + if (uls != 0) uls = (a-uls); + rd->s_start = (uint8_t *)rd->src + uls; + rd->d_start = (uint8_t *)rd->dest + uls; + bytes -= uls; + bytes -= (bytes % align); + rd->s_top = (uint8_t *)rd->s_start + bytes; + rd->d_top = (uint8_t *)rd->d_start + bytes; + +} + +void gf_do_initial_region_alignment(gf_region_data *rd) +{ + gf_slow_multiply_region(rd, rd->src, rd->dest, rd->s_start); +} + +void gf_do_final_region_alignment(gf_region_data *rd) +{ + gf_slow_multiply_region(rd, rd->s_top, rd->d_top, (uint8_t *)rd->src+rd->bytes); +} + +void gf_multby_zero(void *dest, int bytes, int xor) +{ + if (xor) return; + bzero(dest, bytes); + return; +} + +/* JSP - gf_multby_one tries to do this in the most efficient way + possible. If xor = 0, then simply call memcpy() since that + should be optimized by the system. Otherwise, try to do the xor + in the following order: + + If src and dest are aligned with respect to each other on 16-byte + boundaries and you have SSE instructions, then use aligned SSE + instructions. + + If they aren't but you still have SSE instructions, use unaligned + SSE instructions. + + If there are no SSE instructions, but they are aligned with + respect to each other on 8-byte boundaries, then do them with + uint64_t's. + + Otherwise, call gf_unaligned_xor(), which does the following: + align a destination pointer along an 8-byte boundary, and then + memcpy 32 bytes at a time from the src pointer to an array of + doubles. I'm not sure if that's the best -- probably needs + testing, but this seems like it could be a black hole. + */ + +static void gf_unaligned_xor(void *src, void *dest, int bytes); + +void gf_multby_one(void *src, void *dest, int bytes, int xor) +{ + unsigned long uls, uld; + uint8_t *s8, *d8; + uint64_t *s64, *d64, *dtop64; + gf_region_data rd; + + if (!xor) { + if (dest != src) + memcpy(dest, src, bytes); + return; + } + uls = (unsigned long) src; + uld = (unsigned long) dest; + +#ifdef INTEL_SSE2 + if (gf_cpu_supports_intel_sse2) { + __m128i ms, md; + int abytes; + s8 = (uint8_t *) src; + d8 = (uint8_t *) dest; + if (uls % 16 == uld % 16) { + gf_set_region_data(&rd, NULL, src, dest, bytes, 1, xor, 16); + while (s8 != rd.s_start) { + *d8 ^= *s8; + d8++; + s8++; + } + while (s8 < (uint8_t *) rd.s_top) { + ms = _mm_load_si128 ((__m128i *)(s8)); + md = _mm_load_si128 ((__m128i *)(d8)); + md = _mm_xor_si128(md, ms); + _mm_store_si128((__m128i *)(d8), md); + s8 += 16; + d8 += 16; + } + while (s8 != (uint8_t *) src + bytes) { + *d8 ^= *s8; + d8++; + s8++; + } + return; + } + + abytes = (bytes & 0xfffffff0); + + while (d8 < (uint8_t *) dest + abytes) { + ms = _mm_loadu_si128 ((__m128i *)(s8)); + md = _mm_loadu_si128 ((__m128i *)(d8)); + md = _mm_xor_si128(md, ms); + _mm_storeu_si128((__m128i *)(d8), md); + s8 += 16; + d8 += 16; + } + while (d8 != (uint8_t *) dest+bytes) { + *d8 ^= *s8; + d8++; + s8++; + } + return; + } +#endif +#if defined(ARM_NEON) + if (gf_cpu_supports_arm_neon) { + s8 = (uint8_t *) src; + d8 = (uint8_t *) dest; + + if (uls % 16 == uld % 16) { + gf_set_region_data(&rd, NULL, src, dest, bytes, 1, xor, 16); + while (s8 != rd.s_start) { + *d8 ^= *s8; + s8++; + d8++; + } + while (s8 < (uint8_t *) rd.s_top) { + uint8x16_t vs = vld1q_u8 (s8); + uint8x16_t vd = vld1q_u8 (d8); + uint8x16_t vr = veorq_u8 (vs, vd); + vst1q_u8 (d8, vr); + s8 += 16; + d8 += 16; + } + } else { + while (s8 + 15 < (uint8_t *) src + bytes) { + uint8x16_t vs = vld1q_u8 (s8); + uint8x16_t vd = vld1q_u8 (d8); + uint8x16_t vr = veorq_u8 (vs, vd); + vst1q_u8 (d8, vr); + s8 += 16; + d8 += 16; + } + } + while (s8 < (uint8_t *) src + bytes) { + *d8 ^= *s8; + s8++; + d8++; + } + return; + } +#endif + if (uls % 8 != uld % 8) { + gf_unaligned_xor(src, dest, bytes); + return; + } + + gf_set_region_data(&rd, NULL, src, dest, bytes, 1, xor, 8); + s8 = (uint8_t *) src; + d8 = (uint8_t *) dest; + while (d8 != rd.d_start) { + *d8 ^= *s8; + d8++; + s8++; + } + dtop64 = (uint64_t *) rd.d_top; + + d64 = (uint64_t *) rd.d_start; + s64 = (uint64_t *) rd.s_start; + + while (d64 < dtop64) { + *d64 ^= *s64; + d64++; + s64++; + } + + s8 = (uint8_t *) rd.s_top; + d8 = (uint8_t *) rd.d_top; + + while (d8 != (uint8_t *) dest+bytes) { + *d8 ^= *s8; + d8++; + s8++; + } + return; +} + +#define UNALIGNED_BUFSIZE (8) + +static void gf_unaligned_xor(void *src, void *dest, int bytes) +{ + uint64_t scopy[UNALIGNED_BUFSIZE], *d64; + int i; + gf_region_data rd; + uint8_t *s8, *d8; + + /* JSP - call gf_set_region_data(), but use dest in both places. This is + because I only want to set up dest. If I used src, gf_set_region_data() + would fail because src and dest are not aligned to each other wrt + 8-byte pointers. I know this will actually align d_start to 16 bytes. + If I change gf_set_region_data() to split alignment & chunksize, then + I could do this correctly. */ + + gf_set_region_data(&rd, NULL, dest, dest, bytes, 1, 1, 8*UNALIGNED_BUFSIZE); + s8 = (uint8_t *) src; + d8 = (uint8_t *) dest; + + while (d8 < (uint8_t *) rd.d_start) { + *d8 ^= *s8; + d8++; + s8++; + } + + d64 = (uint64_t *) d8; + while (d64 < (uint64_t *) rd.d_top) { + memcpy(scopy, s8, 8*UNALIGNED_BUFSIZE); + s8 += 8*UNALIGNED_BUFSIZE; + for (i = 0; i < UNALIGNED_BUFSIZE; i++) { + *d64 ^= scopy[i]; + d64++; + } + } + + d8 = (uint8_t *) d64; + while (d8 < (uint8_t *) ((uint8_t *)dest+bytes)) { + *d8 ^= *s8; + d8++; + s8++; + } +} diff --git a/IDA_new/gf-complete/src/gf_cpu.c b/IDA_new/gf-complete/src/gf_cpu.c new file mode 100644 index 0000000..f65131f --- /dev/null +++ b/IDA_new/gf-complete/src/gf_cpu.c @@ -0,0 +1,180 @@ +/* + * GF-Complete: A Comprehensive Open Source Library for Galois Field Arithmetic + * James S. Plank, Ethan L. Miller, Kevin M. Greenan, + * Benjamin A. Arnold, John A. Burnum, Adam W. Disney, Allen C. McBride. + * + * gf_cpu.h + * + * Identifies whether the CPU supports SIMD instructions at runtime. + */ + +#include +#include + +int gf_cpu_identified = 0; + +int gf_cpu_supports_intel_pclmul = 0; +int gf_cpu_supports_intel_sse4 = 0; +int gf_cpu_supports_intel_ssse3 = 0; +int gf_cpu_supports_intel_sse3 = 0; +int gf_cpu_supports_intel_sse2 = 0; +int gf_cpu_supports_arm_neon = 0; + +#if defined(__x86_64__) + +/* CPUID Feature Bits */ + +/* ECX */ +#define GF_CPU_SSE3 (1 << 0) +#define GF_CPU_PCLMUL (1 << 1) +#define GF_CPU_SSSE3 (1 << 9) +#define GF_CPU_SSE41 (1 << 19) +#define GF_CPU_SSE42 (1 << 20) + +/* EDX */ +#define GF_CPU_SSE2 (1 << 26) + +#if defined(_MSC_VER) + +#define cpuid(info, x) __cpuidex(info, x, 0) + +#elif defined(__GNUC__) + +#include +void cpuid(int info[4], int InfoType){ + __cpuid_count(InfoType, 0, info[0], info[1], info[2], info[3]); +} + +#else + +#error please add a way to detect CPU SIMD support at runtime + +#endif + +void gf_cpu_identify(void) +{ + if (gf_cpu_identified) { + return; + } + + int reg[4]; + + cpuid(reg, 1); + +#if defined(INTEL_SSE4_PCLMUL) + if ((reg[2] & GF_CPU_PCLMUL) != 0 && !getenv("GF_COMPLETE_DISABLE_SSE4_PCLMUL")) { + gf_cpu_supports_intel_pclmul = 1; +#ifdef DEBUG_CPU_DETECTION + printf("#gf_cpu_supports_intel_pclmul\n"); +#endif + } +#endif + +#if defined(INTEL_SSE4) + if (((reg[2] & GF_CPU_SSE42) != 0 || (reg[2] & GF_CPU_SSE41) != 0) && !getenv("GF_COMPLETE_DISABLE_SSE4")) { + gf_cpu_supports_intel_sse4 = 1; +#ifdef DEBUG_CPU_DETECTION + printf("#gf_cpu_supports_intel_sse4\n"); +#endif + } +#endif + +#if defined(INTEL_SSSE3) + if ((reg[2] & GF_CPU_SSSE3) != 0 && !getenv("GF_COMPLETE_DISABLE_SSSE3")) { + gf_cpu_supports_intel_ssse3 = 1; +#ifdef DEBUG_CPU_DETECTION + printf("#gf_cpu_supports_intel_ssse3\n"); +#endif + } +#endif + +#if defined(INTEL_SSE3) + if ((reg[2] & GF_CPU_SSE3) != 0 && !getenv("GF_COMPLETE_DISABLE_SSE3")) { + gf_cpu_supports_intel_sse3 = 1; +#ifdef DEBUG_CPU_DETECTION + printf("#gf_cpu_supports_intel_sse3\n"); +#endif + } +#endif + +#if defined(INTEL_SSE2) + if ((reg[3] & GF_CPU_SSE2) != 0 && !getenv("GF_COMPLETE_DISABLE_SSE2")) { + gf_cpu_supports_intel_sse2 = 1; +#ifdef DEBUG_CPU_DETECTION + printf("#gf_cpu_supports_intel_sse2\n"); +#endif + } +#endif + + gf_cpu_identified = 1; +} + +#elif defined(__arm__) || defined(__aarch64__) + +#ifdef __linux__ + +#include +#include +#include +#include +#include +#include + +unsigned long get_hwcap(unsigned long type) { + unsigned long hwcap = 0; + int fd = open("/proc/self/auxv", O_RDONLY); + if (fd > 0) { + Elf32_auxv_t auxv; + while (read(fd, &auxv, sizeof(Elf32_auxv_t))) { + if (auxv.a_type == type) { + hwcap = auxv.a_un.a_val; + break; + } + } + close(fd); + } + + return hwcap; +} + +#endif // linux + +void gf_cpu_identify(void) +{ + if (gf_cpu_identified) { + return; + } + +#if defined(ARM_NEON) + if (!getenv("GF_COMPLETE_DISABLE_NEON")) { +#if __linux__ && __arm__ + gf_cpu_supports_arm_neon = (get_hwcap(AT_HWCAP) & HWCAP_NEON) > 0; +#elif __aarch64__ + // ASIMD is supported on all aarch64 architectures + gf_cpu_supports_arm_neon = 1; +#else + // we assume that NEON is supported if the compiler supports + // NEON and we dont have a reliable way to detect runtime support. + gf_cpu_supports_arm_neon = 1; +#endif + +#ifdef DEBUG_CPU_DETECTION + if (gf_cpu_supports_arm_neon) { + printf("#gf_cpu_supports_arm_neon\n"); + } +#endif + } +#endif // defined(ARM_NEON) + + gf_cpu_identified = 1; +} + +#else // defined(__arm__) || defined(__aarch64__) + +int gf_cpu_identify(void) +{ + gf_cpu_identified = 1; + return 0; +} + +#endif diff --git a/IDA_new/gf-complete/src/gf_general.c b/IDA_new/gf-complete/src/gf_general.c new file mode 100644 index 0000000..769f7a0 --- /dev/null +++ b/IDA_new/gf-complete/src/gf_general.c @@ -0,0 +1,539 @@ +/* + * GF-Complete: A Comprehensive Open Source Library for Galois Field Arithmetic + * James S. Plank, Ethan L. Miller, Kevin M. Greenan, + * Benjamin A. Arnold, John A. Burnum, Adam W. Disney, Allen C. McBride. + * + * gf_general.c + * + * This file has helper routines for doing basic GF operations with any + * legal value of w. The problem is that w <= 32, w=64 and w=128 all have + * different data types, which is a pain. The procedures in this file try + * to alleviate that pain. They are used in gf_unit and gf_time. + */ + +#include +#include +#include +#include +#include +#include +#include + +#include "gf_complete.h" +#include "gf_int.h" +#include "gf_method.h" +#include "gf_rand.h" +#include "gf_general.h" + +void gf_general_set_zero(gf_general_t *v, int w) +{ + if (w <= 32) { + v->w32 = 0; + } else if (w <= 64) { + v->w64 = 0; + } else { + v->w128[0] = 0; + v->w128[1] = 0; + } +} + +void gf_general_set_one(gf_general_t *v, int w) +{ + if (w <= 32) { + v->w32 = 1; + } else if (w <= 64) { + v->w64 = 1; + } else { + v->w128[0] = 0; + v->w128[1] = 1; + } +} + +void gf_general_set_two(gf_general_t *v, int w) +{ + if (w <= 32) { + v->w32 = 2; + } else if (w <= 64) { + v->w64 = 2; + } else { + v->w128[0] = 0; + v->w128[1] = 2; + } +} + +int gf_general_is_zero(gf_general_t *v, int w) +{ + if (w <= 32) { + return (v->w32 == 0); + } else if (w <= 64) { + return (v->w64 == 0); + } else { + return (v->w128[0] == 0 && v->w128[1] == 0); + } +} + +int gf_general_is_one(gf_general_t *v, int w) +{ + if (w <= 32) { + return (v->w32 == 1); + } else if (w <= 64) { + return (v->w64 == 1); + } else { + return (v->w128[0] == 0 && v->w128[1] == 1); + } +} + +void gf_general_set_random(gf_general_t *v, int w, int zero_ok) +{ + if (w <= 32) { + v->w32 = MOA_Random_W(w, zero_ok); + } else if (w <= 64) { + while (1) { + v->w64 = MOA_Random_64(); + if (v->w64 != 0 || zero_ok) return; + } + } else { + while (1) { + MOA_Random_128(v->w128); + if (v->w128[0] != 0 || v->w128[1] != 0 || zero_ok) return; + } + } +} + +void gf_general_val_to_s(gf_general_t *v, int w, char *s, int hex) +{ + if (w <= 32) { + if (hex) { + sprintf(s, "%x", v->w32); + } else { + sprintf(s, "%u", v->w32); + } + } else if (w <= 64) { + if (hex) { + sprintf(s, "%llx", (long long unsigned int) v->w64); + } else { + sprintf(s, "%lld", (long long unsigned int) v->w64); + } + } else { + if (v->w128[0] == 0) { + sprintf(s, "%llx", (long long unsigned int) v->w128[1]); + } else { + sprintf(s, "%llx%016llx", (long long unsigned int) v->w128[0], + (long long unsigned int) v->w128[1]); + } + } +} + +int gf_general_s_to_val(gf_general_t *v, int w, char *s, int hex) +{ + int l; + int save; + + if (w <= 32) { + if (hex) { + if (sscanf(s, "%x", &(v->w32)) == 0) return 0; + } else { + if (sscanf(s, "%u", &(v->w32)) == 0) return 0; + } + if (w == 32) return 1; + if (w == 31) { + if (v->w32 & ((gf_val_32_t)1 << 31)) return 0; + return 1; + } + if (v->w32 & ~((1 << w)-1)) return 0; + return 1; + } else if (w <= 64) { + if (hex) return (sscanf(s, "%llx", (long long unsigned int *) (&(v->w64))) == 1); + return (sscanf(s, "%lld", (long long int *) (&(v->w64))) == 1); + } else { + if (!hex) return 0; + l = strlen(s); + if (l <= 16) { + v->w128[0] = 0; + return (sscanf(s, "%llx", (long long unsigned int *) (&(v->w128[1]))) == 1); + } else { + if (l > 32) return 0; + save = s[l-16]; + s[l-16] = '\0'; + if (sscanf(s, "%llx", (long long unsigned int *) (&(v->w128[0]))) == 0) { + s[l-16] = save; + return 0; + } + return (sscanf(s+(l-16), "%llx", (long long unsigned int *) (&(v->w128[1]))) == 1); + } + } +} + +void gf_general_add(gf_t *gf, gf_general_t *a, gf_general_t *b, gf_general_t *c) +{ + gf_internal_t *h; + int w; + + h = (gf_internal_t *) gf->scratch; + w = h->w; + + if (w <= 32) { + c->w32 = a->w32 ^ b->w32; + } else if (w <= 64) { + c->w64 = a->w64 ^ b->w64; + } else { + c->w128[0] = a->w128[0] ^ b->w128[0]; + c->w128[1] = a->w128[1] ^ b->w128[1]; + } +} + +void gf_general_multiply(gf_t *gf, gf_general_t *a, gf_general_t *b, gf_general_t *c) +{ + gf_internal_t *h; + int w; + + h = (gf_internal_t *) gf->scratch; + w = h->w; + + if (w <= 32) { + c->w32 = gf->multiply.w32(gf, a->w32, b->w32); + } else if (w <= 64) { + c->w64 = gf->multiply.w64(gf, a->w64, b->w64); + } else { + gf->multiply.w128(gf, a->w128, b->w128, c->w128); + } +} + +void gf_general_divide(gf_t *gf, gf_general_t *a, gf_general_t *b, gf_general_t *c) +{ + gf_internal_t *h; + int w; + + h = (gf_internal_t *) gf->scratch; + w = h->w; + + if (w <= 32) { + c->w32 = gf->divide.w32(gf, a->w32, b->w32); + } else if (w <= 64) { + c->w64 = gf->divide.w64(gf, a->w64, b->w64); + } else { + gf->divide.w128(gf, a->w128, b->w128, c->w128); + } +} + +void gf_general_inverse(gf_t *gf, gf_general_t *a, gf_general_t *b) +{ + gf_internal_t *h; + int w; + + h = (gf_internal_t *) gf->scratch; + w = h->w; + + if (w <= 32) { + b->w32 = gf->inverse.w32(gf, a->w32); + } else if (w <= 64) { + b->w64 = gf->inverse.w64(gf, a->w64); + } else { + gf->inverse.w128(gf, a->w128, b->w128); + } +} + +int gf_general_are_equal(gf_general_t *v1, gf_general_t *v2, int w) +{ + if (w <= 32) { + return (v1->w32 == v2->w32); + } else if (w <= 64) { + return (v1->w64 == v2->w64); + } else { + return (v1->w128[0] == v2->w128[0] && + v1->w128[1] == v2->w128[1]); + } +} + +void gf_general_do_region_multiply(gf_t *gf, gf_general_t *a, void *ra, void *rb, int bytes, int xor) +{ + gf_internal_t *h; + int w; + + h = (gf_internal_t *) gf->scratch; + w = h->w; + + if (w <= 32) { + gf->multiply_region.w32(gf, ra, rb, a->w32, bytes, xor); + } else if (w <= 64) { + gf->multiply_region.w64(gf, ra, rb, a->w64, bytes, xor); + } else { + gf->multiply_region.w128(gf, ra, rb, a->w128, bytes, xor); + } +} + +void gf_general_do_region_check(gf_t *gf, gf_general_t *a, void *orig_a, void *orig_target, void *final_target, int bytes, int xor) +{ + gf_internal_t *h; + int w, words, i; + gf_general_t oa, ot, ft, sb; + char sa[50], soa[50], sot[50], sft[50], ssb[50]; + + h = (gf_internal_t *) gf->scratch; + w = h->w; + + words = (bytes * 8) / w; + for (i = 0; i < words; i++) { + if (w <= 32) { + oa.w32 = gf->extract_word.w32(gf, orig_a, bytes, i); + ot.w32 = gf->extract_word.w32(gf, orig_target, bytes, i); + ft.w32 = gf->extract_word.w32(gf, final_target, bytes, i); + sb.w32 = gf->multiply.w32(gf, a->w32, oa.w32); + if (xor) sb.w32 ^= ot.w32; + } else if (w <= 64) { + oa.w64 = gf->extract_word.w64(gf, orig_a, bytes, i); + ot.w64 = gf->extract_word.w64(gf, orig_target, bytes, i); + ft.w64 = gf->extract_word.w64(gf, final_target, bytes, i); + sb.w64 = gf->multiply.w64(gf, a->w64, oa.w64); + if (xor) sb.w64 ^= ot.w64; + } else { + gf->extract_word.w128(gf, orig_a, bytes, i, oa.w128); + gf->extract_word.w128(gf, orig_target, bytes, i, ot.w128); + gf->extract_word.w128(gf, final_target, bytes, i, ft.w128); + gf->multiply.w128(gf, a->w128, oa.w128, sb.w128); + if (xor) { + sb.w128[0] ^= ot.w128[0]; + sb.w128[1] ^= ot.w128[1]; + } + } + + if (!gf_general_are_equal(&ft, &sb, w)) { + + fprintf(stderr,"Problem with region multiply (all values in hex):\n"); + fprintf(stderr," Target address base: 0x%lx. Word 0x%x of 0x%x. Xor: %d\n", + (unsigned long) final_target, i, words, xor); + gf_general_val_to_s(a, w, sa, 1); + gf_general_val_to_s(&oa, w, soa, 1); + gf_general_val_to_s(&ot, w, sot, 1); + gf_general_val_to_s(&ft, w, sft, 1); + gf_general_val_to_s(&sb, w, ssb, 1); + fprintf(stderr," Value: %s\n", sa); + fprintf(stderr," Original source word: %s\n", soa); + if (xor) fprintf(stderr," XOR with target word: %s\n", sot); + fprintf(stderr," Product word: %s\n", sft); + fprintf(stderr," It should be: %s\n", ssb); + assert(0); + } + } +} + +void gf_general_set_up_single_timing_test(int w, void *ra, void *rb, int size) +{ + void *top; + gf_general_t g; + uint8_t *r8, *r8a; + uint16_t *r16; + uint32_t *r32; + uint64_t *r64; + int i; + + top = (uint8_t *)rb+size; + + /* If w is 8, 16, 32, 64 or 128, fill the regions with random bytes. + However, don't allow for zeros in rb, because that will screw up + division. + + When w is 4, you fill the regions with random 4-bit words in each byte. + + Otherwise, treat every four bytes as an uint32_t + and fill it with a random value mod (1 << w). + */ + + if (w == 8 || w == 16 || w == 32 || w == 64 || w == 128) { + MOA_Fill_Random_Region (ra, size); + while (rb < top) { + gf_general_set_random(&g, w, 0); + switch (w) { + case 8: + r8 = (uint8_t *) rb; + *r8 = g.w32; + break; + case 16: + r16 = (uint16_t *) rb; + *r16 = g.w32; + break; + case 32: + r32 = (uint32_t *) rb; + *r32 = g.w32; + break; + case 64: + r64 = (uint64_t *) rb; + *r64 = g.w64; + break; + case 128: + r64 = (uint64_t *) rb; + r64[0] = g.w128[0]; + r64[1] = g.w128[1]; + break; + } + rb = (uint8_t *)rb + (w/8); + } + } else if (w == 4) { + r8a = (uint8_t *) ra; + r8 = (uint8_t *) rb; + while (r8 < (uint8_t *) top) { + gf_general_set_random(&g, w, 1); + *r8a = g.w32; + gf_general_set_random(&g, w, 0); + *r8 = g.w32; + r8a++; + r8++; + } + } else { + r32 = (uint32_t *) ra; + for (i = 0; i < size/4; i++) r32[i] = MOA_Random_W(w, 1); + r32 = (uint32_t *) rb; + for (i = 0; i < size/4; i++) r32[i] = MOA_Random_W(w, 0); + } +} + +/* This sucks, but in order to time, you really need to avoid putting ifs in + the inner loops. So, I'm doing a separate timing test for each w: + (4 & 8), 16, 32, 64, 128 and everything else. Fortunately, the "everything else" + tests can be equivalent to w=32. + + I'm also putting the results back into ra, because otherwise, the optimizer might + figure out that we're not really doing anything in the inner loops and it + will chuck that. */ + +int gf_general_do_single_timing_test(gf_t *gf, void *ra, void *rb, int size, char test) +{ + gf_internal_t *h; + void *top; + uint8_t *r8a, *r8b, *top8; + uint16_t *r16a, *r16b, *top16; + uint32_t *r32a, *r32b, *top32; + uint64_t *r64a, *r64b, *top64, *r64c; + int w, rv; + + h = (gf_internal_t *) gf->scratch; + w = h->w; + top = (uint8_t *)ra + size; + + if (w == 8 || w == 4) { + r8a = (uint8_t *) ra; + r8b = (uint8_t *) rb; + top8 = (uint8_t *) top; + if (test == 'M') { + while (r8a < top8) { + *r8a = gf->multiply.w32(gf, *r8a, *r8b); + r8a++; + r8b++; + } + } else if (test == 'D') { + while (r8a < top8) { + *r8a = gf->divide.w32(gf, *r8a, *r8b); + r8a++; + r8b++; + } + } else if (test == 'I') { + while (r8a < top8) { + *r8a = gf->inverse.w32(gf, *r8a); + r8a++; + } + } + return (top8 - (uint8_t *) ra); + } + + if (w == 16) { + r16a = (uint16_t *) ra; + r16b = (uint16_t *) rb; + top16 = (uint16_t *) top; + if (test == 'M') { + while (r16a < top16) { + *r16a = gf->multiply.w32(gf, *r16a, *r16b); + r16a++; + r16b++; + } + } else if (test == 'D') { + while (r16a < top16) { + *r16a = gf->divide.w32(gf, *r16a, *r16b); + r16a++; + r16b++; + } + } else if (test == 'I') { + while (r16a < top16) { + *r16a = gf->inverse.w32(gf, *r16a); + r16a++; + } + } + return (top16 - (uint16_t *) ra); + } + if (w <= 32) { + r32a = (uint32_t *) ra; + r32b = (uint32_t *) rb; + top32 = (uint32_t *) ra + (size/4); /* This is for the "everything elses" */ + + if (test == 'M') { + while (r32a < top32) { + *r32a = gf->multiply.w32(gf, *r32a, *r32b); + r32a++; + r32b++; + } + } else if (test == 'D') { + while (r32a < top32) { + *r32a = gf->divide.w32(gf, *r32a, *r32b); + r32a++; + r32b++; + } + } else if (test == 'I') { + while (r32a < top32) { + *r32a = gf->inverse.w32(gf, *r32a); + r32a++; + } + } + return (top32 - (uint32_t *) ra); + } + if (w == 64) { + r64a = (uint64_t *) ra; + r64b = (uint64_t *) rb; + top64 = (uint64_t *) top; + if (test == 'M') { + while (r64a < top64) { + *r64a = gf->multiply.w64(gf, *r64a, *r64b); + r64a++; + r64b++; + } + } else if (test == 'D') { + while (r64a < top64) { + *r64a = gf->divide.w64(gf, *r64a, *r64b); + r64a++; + r64b++; + } + } else if (test == 'I') { + while (r64a < top64) { + *r64a = gf->inverse.w64(gf, *r64a); + r64a++; + } + } + return (top64 - (uint64_t *) ra); + } + if (w == 128) { + r64a = (uint64_t *) ra; + r64c = r64a; + r64a += 2; + r64b = (uint64_t *) rb; + top64 = (uint64_t *) top; + rv = (top64 - r64a)/2; + if (test == 'M') { + while (r64a < top64) { + gf->multiply.w128(gf, r64a, r64b, r64c); + r64a += 2; + r64b += 2; + } + } else if (test == 'D') { + while (r64a < top64) { + gf->divide.w128(gf, r64a, r64b, r64c); + r64a += 2; + r64b += 2; + } + } else if (test == 'I') { + while (r64a < top64) { + gf->inverse.w128(gf, r64a, r64c); + r64a += 2; + } + } + return rv; + } + return 0; +} diff --git a/IDA_new/gf-complete/src/gf_method.c b/IDA_new/gf-complete/src/gf_method.c new file mode 100644 index 0000000..2210305 --- /dev/null +++ b/IDA_new/gf-complete/src/gf_method.c @@ -0,0 +1,193 @@ +/* + * GF-Complete: A Comprehensive Open Source Library for Galois Field Arithmetic + * James S. Plank, Ethan L. Miller, Kevin M. Greenan, + * Benjamin A. Arnold, John A. Burnum, Adam W. Disney, Allen C. McBride. + * + * gf_method.c + * + * Parses argv to figure out the mult_type and arguments. Returns the gf. + */ + +#include +#include +#include +#include +#include + +#include "gf_complete.h" +#include "gf_int.h" +#include "gf_method.h" + +int create_gf_from_argv(gf_t *gf, int w, int argc, char **argv, int starting) +{ + int mult_type, divide_type, region_type; + int arg1, arg2; + uint64_t prim_poly; + gf_t *base; + + mult_type = GF_MULT_DEFAULT; + region_type = GF_REGION_DEFAULT; + divide_type = GF_DIVIDE_DEFAULT; + prim_poly = 0; + base = NULL; + arg1 = 0; + arg2 = 0; + while (1) { + if (argc > starting) { + if (strcmp(argv[starting], "-m") == 0) { + starting++; + if (mult_type != GF_MULT_DEFAULT) { + if (base != NULL) gf_free(base, 1); + _gf_errno = GF_E_TWOMULT; + return 0; + } + if (strcmp(argv[starting], "SHIFT") == 0) { + mult_type = GF_MULT_SHIFT; + starting++; + } else if (strcmp(argv[starting], "CARRY_FREE") == 0) { + mult_type = GF_MULT_CARRY_FREE; + starting++; + } else if (strcmp(argv[starting], "CARRY_FREE_GK") == 0) { + mult_type = GF_MULT_CARRY_FREE_GK; + starting++; + } else if (strcmp(argv[starting], "GROUP") == 0) { + mult_type = GF_MULT_GROUP; + if (argc < starting + 3) { + _gf_errno = GF_E_GROUPAR; + return 0; + } + if (sscanf(argv[starting+1], "%d", &arg1) == 0 || + sscanf(argv[starting+2], "%d", &arg2) == 0) { + _gf_errno = GF_E_GROUPNU; + return 0; + } + starting += 3; + } else if (strcmp(argv[starting], "BYTWO_p") == 0) { + mult_type = GF_MULT_BYTWO_p; + starting++; + } else if (strcmp(argv[starting], "BYTWO_b") == 0) { + mult_type = GF_MULT_BYTWO_b; + starting++; + } else if (strcmp(argv[starting], "TABLE") == 0) { + mult_type = GF_MULT_TABLE; + starting++; + } else if (strcmp(argv[starting], "LOG") == 0) { + mult_type = GF_MULT_LOG_TABLE; + starting++; + } else if (strcmp(argv[starting], "LOG_ZERO") == 0) { + mult_type = GF_MULT_LOG_ZERO; + starting++; + } else if (strcmp(argv[starting], "LOG_ZERO_EXT") == 0) { + mult_type = GF_MULT_LOG_ZERO_EXT; + starting++; + } else if (strcmp(argv[starting], "SPLIT") == 0) { + mult_type = GF_MULT_SPLIT_TABLE; + if (argc < starting + 3) { + _gf_errno = GF_E_SPLITAR; + return 0; + } + if (sscanf(argv[starting+1], "%d", &arg1) == 0 || + sscanf(argv[starting+2], "%d", &arg2) == 0) { + _gf_errno = GF_E_SPLITNU; + return 0; + } + starting += 3; + } else if (strcmp(argv[starting], "COMPOSITE") == 0) { + mult_type = GF_MULT_COMPOSITE; + if (argc < starting + 2) { _gf_errno = GF_E_FEWARGS; return 0; } + if (sscanf(argv[starting+1], "%d", &arg1) == 0) { + _gf_errno = GF_E_COMP_A2; + return 0; + } + starting += 2; + base = (gf_t *) malloc(sizeof(gf_t)); + starting = create_gf_from_argv(base, w/arg1, argc, argv, starting); + if (starting == 0) { + free(base); + return 0; + } + } else { + _gf_errno = GF_E_UNKNOWN; + return 0; + } + } else if (strcmp(argv[starting], "-r") == 0) { + starting++; + if (strcmp(argv[starting], "DOUBLE") == 0) { + region_type |= GF_REGION_DOUBLE_TABLE; + starting++; + } else if (strcmp(argv[starting], "QUAD") == 0) { + region_type |= GF_REGION_QUAD_TABLE; + starting++; + } else if (strcmp(argv[starting], "LAZY") == 0) { + region_type |= GF_REGION_LAZY; + starting++; + } else if (strcmp(argv[starting], "SIMD") == 0) { + region_type |= GF_REGION_SIMD; + starting++; + } else if (strcmp(argv[starting], "NOSIMD") == 0) { + region_type |= GF_REGION_NOSIMD; + starting++; + } else if (strcmp(argv[starting], "SSE") == 0) { + region_type |= GF_REGION_SIMD; + starting++; + } else if (strcmp(argv[starting], "NOSSE") == 0) { + region_type |= GF_REGION_NOSIMD; + starting++; + } else if (strcmp(argv[starting], "CAUCHY") == 0) { + region_type |= GF_REGION_CAUCHY; + starting++; + } else if (strcmp(argv[starting], "ALTMAP") == 0) { + region_type |= GF_REGION_ALTMAP; + starting++; + } else { + if (base != NULL) gf_free(base, 1); + _gf_errno = GF_E_UNK_REG; + return 0; + } + } else if (strcmp(argv[starting], "-p") == 0) { + starting++; + if (sscanf(argv[starting], "%llx", (long long unsigned int *)(&prim_poly)) == 0) { + if (base != NULL) gf_free(base, 1); + _gf_errno = GF_E_POLYSPC; + return 0; + } + starting++; + } else if (strcmp(argv[starting], "-d") == 0) { + starting++; + if (divide_type != GF_DIVIDE_DEFAULT) { + if (base != NULL) gf_free(base, 1); + _gf_errno = GF_E_TWO_DIV; + return 0; + } else if (strcmp(argv[starting], "EUCLID") == 0) { + divide_type = GF_DIVIDE_EUCLID; + starting++; + } else if (strcmp(argv[starting], "MATRIX") == 0) { + divide_type = GF_DIVIDE_MATRIX; + starting++; + } else { + _gf_errno = GF_E_UNK_DIV; + return 0; + } + } else if (strcmp(argv[starting], "-") == 0) { + /* + printf("Scratch size: %d\n", gf_scratch_size(w, + mult_type, region_type, divide_type, arg1, arg2)); + */ + if (gf_init_hard(gf, w, mult_type, region_type, divide_type, + prim_poly, arg1, arg2, base, NULL) == 0) { + if (base != NULL) gf_free(base, 1); + return 0; + } else + return starting + 1; + } else { + if (base != NULL) gf_free(base, 1); + _gf_errno = GF_E_UNKFLAG; + return 0; + } + } else { + if (base != NULL) gf_free(base, 1); + _gf_errno = GF_E_FEWARGS; + return 0; + } + } +} diff --git a/IDA_new/gf-complete/src/gf_rand.c b/IDA_new/gf-complete/src/gf_rand.c new file mode 100644 index 0000000..a9aa7ad --- /dev/null +++ b/IDA_new/gf-complete/src/gf_rand.c @@ -0,0 +1,80 @@ +/* + * GF-Complete: A Comprehensive Open Source Library for Galois Field Arithmetic + * James S. Plank, Ethan L. Miller, Kevin M. Greenan, + * Benjamin A. Arnold, John A. Burnum, Adam W. Disney, Allen C. McBride. + * + * gf_rand.c -- Random number generator. + */ + +#include +#include +#include +#include "gf_rand.h" + +/* Lifted the "Mother of All" random number generator from http://www.agner.org/random/ */ + +static uint32_t MOA_X[5]; + +uint32_t MOA_Random_32() { + uint64_t sum; + sum = (uint64_t)2111111111UL * (uint64_t)MOA_X[3] + + (uint64_t)1492 * (uint64_t)(MOA_X[2]) + + (uint64_t)1776 * (uint64_t)(MOA_X[1]) + + (uint64_t)5115 * (uint64_t)(MOA_X[0]) + + (uint64_t)MOA_X[4]; + MOA_X[3] = MOA_X[2]; MOA_X[2] = MOA_X[1]; MOA_X[1] = MOA_X[0]; + MOA_X[4] = (uint32_t)(sum >> 32); + MOA_X[0] = (uint32_t)sum; + return MOA_X[0]; +} + +uint64_t MOA_Random_64() { + uint64_t sum; + + sum = MOA_Random_32(); + sum <<= 32; + sum |= MOA_Random_32(); + return sum; +} + +void MOA_Random_128(uint64_t *x) { + x[0] = MOA_Random_64(); + x[1] = MOA_Random_64(); + return; +} + +uint32_t MOA_Random_W(int w, int zero_ok) +{ + uint32_t b; + + do { + b = MOA_Random_32(); + if (w == 31) b &= 0x7fffffff; + if (w < 31) b %= (1 << w); + } while (!zero_ok && b == 0); + return b; +} + +void MOA_Seed(uint32_t seed) { + int i; + uint32_t s = seed; + for (i = 0; i < 5; i++) { + s = s * 29943829 - 1; + MOA_X[i] = s; + } + for (i=0; i<19; i++) MOA_Random_32(); +} + + +void MOA_Fill_Random_Region (void *reg, int size) +{ + uint32_t *r32; + uint8_t *r8; + int i; + + r32 = (uint32_t *) reg; + r8 = (uint8_t *) reg; + for (i = 0; i < size/4; i++) r32[i] = MOA_Random_32(); + for (i *= 4; i < size; i++) r8[i] = MOA_Random_W(8, 1); +} + diff --git a/IDA_new/gf-complete/src/gf_w128.c b/IDA_new/gf-complete/src/gf_w128.c new file mode 100644 index 0000000..3bc2d65 --- /dev/null +++ b/IDA_new/gf-complete/src/gf_w128.c @@ -0,0 +1,1776 @@ +/* + * GF-Complete: A Comprehensive Open Source Library for Galois Field Arithmetic + * James S. Plank, Ethan L. Miller, Kevin M. Greenan, + * Benjamin A. Arnold, John A. Burnum, Adam W. Disney, Allen C. McBride. + * + * gf_w128.c + * + * Routines for 128-bit Galois fields + */ + +#include "gf_int.h" +#include +#include +#include "gf_cpu.h" + +#define GF_FIELD_WIDTH (128) + +#define two_x(a) {\ + a[0] <<= 1; \ + if (a[1] & 1ULL << 63) a[0] ^= 1; \ + a[1] <<= 1; } + +#define a_get_b(a, i, b, j) {\ + a[i] = b[j]; \ + a[i + 1] = b[j + 1];} + +#define set_zero(a, i) {\ + a[i] = 0; \ + a[i + 1] = 0;} + +struct gf_w128_split_4_128_data { + uint64_t last_value[2]; + uint64_t tables[2][32][16]; +}; + +struct gf_w128_split_8_128_data { + uint64_t last_value[2]; + uint64_t tables[2][16][256]; +}; + +typedef struct gf_group_tables_s { + gf_val_128_t m_table; + gf_val_128_t r_table; +} gf_group_tables_t; + +#define MM_PRINT8(s, r) { uint8_t blah[16], ii; printf("%-12s", s); _mm_storeu_si128((__m128i *)blah, r); for (ii = 0; ii < 16; ii += 1) printf("%s%02x", (ii%4==0) ? " " : " ", blah[15-ii]); printf("\n"); } + +static +void +gf_w128_multiply_region_from_single(gf_t *gf, void *src, void *dest, gf_val_128_t val, int bytes, +int xor) +{ + uint32_t i; + gf_val_128_t s128; + gf_val_128_t d128; + uint64_t c128[2]; + gf_region_data rd; + + /* We only do this to check on alignment. */ + gf_set_region_data(&rd, gf, src, dest, bytes, 0, xor, 8); + + if (val[0] == 0) { + if (val[1] == 0) { gf_multby_zero(dest, bytes, xor); return; } + if (val[1] == 1) { gf_multby_one(src, dest, bytes, xor); return; } + } + + set_zero(c128, 0); + + s128 = (gf_val_128_t) src; + d128 = (gf_val_128_t) dest; + + if (xor) { + for (i = 0; i < bytes/sizeof(gf_val_64_t); i += 2) { + gf->multiply.w128(gf, &s128[i], val, c128); + d128[i] ^= c128[0]; + d128[i+1] ^= c128[1]; + } + } else { + for (i = 0; i < bytes/sizeof(gf_val_64_t); i += 2) { + gf->multiply.w128(gf, &s128[i], val, &d128[i]); + } + } +} + +#if defined(INTEL_SSE4_PCLMUL) +static +void +gf_w128_clm_multiply_region_from_single(gf_t *gf, void *src, void *dest, gf_val_128_t val, int bytes, +int xor) +{ + uint32_t i; + gf_val_128_t s128; + gf_val_128_t d128; + gf_region_data rd; + __m128i a,b; + __m128i result0,result1; + __m128i prim_poly; + __m128i c,d,e,f; + gf_internal_t * h = gf->scratch; + prim_poly = _mm_set_epi32(0, 0, 0, (uint32_t)h->prim_poly); + /* We only do this to check on alignment. */ + gf_set_region_data(&rd, gf, src, dest, bytes, 0, xor, 8); + + if (val[0] == 0) { + if (val[1] == 0) { gf_multby_zero(dest, bytes, xor); return; } + if (val[1] == 1) { gf_multby_one(src, dest, bytes, xor); return; } + } + + s128 = (gf_val_128_t) src; + d128 = (gf_val_128_t) dest; + + if (xor) { + for (i = 0; i < bytes/sizeof(gf_val_64_t); i += 2) { + a = _mm_insert_epi64 (_mm_setzero_si128(), s128[i+1], 0); + b = _mm_insert_epi64 (a, val[1], 0); + a = _mm_insert_epi64 (a, s128[i], 1); + b = _mm_insert_epi64 (b, val[0], 1); + + c = _mm_clmulepi64_si128 (a, b, 0x00); /*low-low*/ + f = _mm_clmulepi64_si128 (a, b, 0x01); /*high-low*/ + e = _mm_clmulepi64_si128 (a, b, 0x10); /*low-high*/ + d = _mm_clmulepi64_si128 (a, b, 0x11); /*high-high*/ + + /* now reusing a and b as temporary variables*/ + result0 = _mm_setzero_si128(); + result1 = result0; + + result0 = _mm_xor_si128 (result0, _mm_insert_epi64 (d, 0, 0)); + a = _mm_xor_si128 (_mm_srli_si128 (e, 8), _mm_insert_epi64 (d, 0, 1)); + result0 = _mm_xor_si128 (result0, _mm_xor_si128 (_mm_srli_si128 (f, 8), a)); + + a = _mm_xor_si128 (_mm_slli_si128 (e, 8), _mm_insert_epi64 (c, 0, 0)); + result1 = _mm_xor_si128 (result1, _mm_xor_si128 (_mm_slli_si128 (f, 8), a)); + result1 = _mm_xor_si128 (result1, _mm_insert_epi64 (c, 0, 1)); + /* now we have constructed our 'result' with result0 being the carry bits, and we have to reduce. */ + + a = _mm_srli_si128 (result0, 8); + b = _mm_clmulepi64_si128 (a, prim_poly, 0x00); + result0 = _mm_xor_si128 (result0, _mm_srli_si128 (b, 8)); + result1 = _mm_xor_si128 (result1, _mm_slli_si128 (b, 8)); + + a = _mm_insert_epi64 (result0, 0, 1); + b = _mm_clmulepi64_si128 (a, prim_poly, 0x00); + result1 = _mm_xor_si128 (result1, b); + d128[i] ^= (uint64_t)_mm_extract_epi64(result1,1); + d128[i+1] ^= (uint64_t)_mm_extract_epi64(result1,0); + } + } else { + for (i = 0; i < bytes/sizeof(gf_val_64_t); i += 2) { + a = _mm_insert_epi64 (_mm_setzero_si128(), s128[i+1], 0); + b = _mm_insert_epi64 (a, val[1], 0); + a = _mm_insert_epi64 (a, s128[i], 1); + b = _mm_insert_epi64 (b, val[0], 1); + + c = _mm_clmulepi64_si128 (a, b, 0x00); /*low-low*/ + f = _mm_clmulepi64_si128 (a, b, 0x01); /*high-low*/ + e = _mm_clmulepi64_si128 (a, b, 0x10); /*low-high*/ + d = _mm_clmulepi64_si128 (a, b, 0x11); /*high-high*/ + + /* now reusing a and b as temporary variables*/ + result0 = _mm_setzero_si128(); + result1 = result0; + + result0 = _mm_xor_si128 (result0, _mm_insert_epi64 (d, 0, 0)); + a = _mm_xor_si128 (_mm_srli_si128 (e, 8), _mm_insert_epi64 (d, 0, 1)); + result0 = _mm_xor_si128 (result0, _mm_xor_si128 (_mm_srli_si128 (f, 8), a)); + + a = _mm_xor_si128 (_mm_slli_si128 (e, 8), _mm_insert_epi64 (c, 0, 0)); + result1 = _mm_xor_si128 (result1, _mm_xor_si128 (_mm_slli_si128 (f, 8), a)); + result1 = _mm_xor_si128 (result1, _mm_insert_epi64 (c, 0, 1)); + /* now we have constructed our 'result' with result0 being the carry bits, and we have to reduce.*/ + + a = _mm_srli_si128 (result0, 8); + b = _mm_clmulepi64_si128 (a, prim_poly, 0x00); + result0 = _mm_xor_si128 (result0, _mm_srli_si128 (b, 8)); + result1 = _mm_xor_si128 (result1, _mm_slli_si128 (b, 8)); + + a = _mm_insert_epi64 (result0, 0, 1); + b = _mm_clmulepi64_si128 (a, prim_poly, 0x00); + result1 = _mm_xor_si128 (result1, b); + d128[i] = (uint64_t)_mm_extract_epi64(result1,1); + d128[i+1] = (uint64_t)_mm_extract_epi64(result1,0); + } + } +} +#endif + +/* + * Some w128 notes: + * --Big Endian + * --return values allocated beforehand + */ + +#define GF_W128_IS_ZERO(val) (val[0] == 0 && val[1] == 0) + +void +gf_w128_shift_multiply(gf_t *gf, gf_val_128_t a128, gf_val_128_t b128, gf_val_128_t c128) +{ + /* ordered highest bit to lowest l[0] l[1] r[0] r[1] */ + uint64_t pl[2], pr[2], ppl[2], ppr[2], i, a[2], bl[2], br[2], one, lbit; + gf_internal_t *h; + + h = (gf_internal_t *) gf->scratch; + + if (GF_W128_IS_ZERO(a128) || GF_W128_IS_ZERO(b128)) { + set_zero(c128, 0); + return; + } + + a_get_b(a, 0, a128, 0); + a_get_b(br, 0, b128, 0); + set_zero(bl, 0); + + one = 1; + lbit = (one << 63); + + set_zero(pl, 0); + set_zero(pr, 0); + + /* Allen: a*b for right half of a */ + for (i = 0; i < GF_FIELD_WIDTH/2; i++) { + if (a[1] & (one << i)) { + pl[1] ^= bl[1]; + pr[0] ^= br[0]; + pr[1] ^= br[1]; + } + bl[1] <<= 1; + if (br[0] & lbit) bl[1] ^= 1; + br[0] <<= 1; + if (br[1] & lbit) br[0] ^= 1; + br[1] <<= 1; + } + + /* Allen: a*b for left half of a */ + for (i = 0; i < GF_FIELD_WIDTH/2; i++) { + if (a[0] & (one << i)) { + pl[0] ^= bl[0]; + pl[1] ^= bl[1]; + pr[0] ^= br[0]; + } + bl[0] <<= 1; + if (bl[1] & lbit) bl[0] ^= 1; + bl[1] <<= 1; + if (br[0] & lbit) bl[1] ^= 1; + br[0] <<= 1; + } + + /* Allen: do first half of reduction (based on left quarter of initial product) */ + one = lbit >> 1; + ppl[0] = one; /* Allen: introduce leading one of primitive polynomial */ + ppl[1] = h->prim_poly >> 2; + ppr[0] = h->prim_poly << (GF_FIELD_WIDTH/2-2); + ppr[1] = 0; + while (one != 0) { + if (pl[0] & one) { + pl[0] ^= ppl[0]; + pl[1] ^= ppl[1]; + pr[0] ^= ppr[0]; + pr[1] ^= ppr[1]; + } + one >>= 1; + ppr[1] >>= 1; + if (ppr[0] & 1) ppr[1] ^= lbit; + ppr[0] >>= 1; + if (ppl[1] & 1) ppr[0] ^= lbit; + ppl[1] >>= 1; + if (ppl[0] & 1) ppl[1] ^= lbit; + ppl[0] >>= 1; + } + + /* Allen: final half of reduction */ + one = lbit; + while (one != 0) { + if (pl[1] & one) { + pl[1] ^= ppl[1]; + pr[0] ^= ppr[0]; + pr[1] ^= ppr[1]; + } + one >>= 1; + ppr[1] >>= 1; + if (ppr[0] & 1) ppr[1] ^= lbit; + ppr[0] >>= 1; + if (ppl[1] & 1) ppr[0] ^= lbit; + ppl[1] >>= 1; + } + + /* Allen: if we really want to optimize this we can just be using c128 instead of pr all along */ + c128[0] = pr[0]; + c128[1] = pr[1]; + + return; +} + +#if defined(INTEL_SSE4_PCLMUL) + +void +gf_w128_clm_multiply(gf_t *gf, gf_val_128_t a128, gf_val_128_t b128, gf_val_128_t c128) +{ + __m128i a,b; + __m128i result0,result1; + __m128i prim_poly; + __m128i c,d,e,f; + gf_internal_t * h = gf->scratch; + + a = _mm_insert_epi64 (_mm_setzero_si128(), a128[1], 0); + b = _mm_insert_epi64 (a, b128[1], 0); + a = _mm_insert_epi64 (a, a128[0], 1); + b = _mm_insert_epi64 (b, b128[0], 1); + + prim_poly = _mm_set_epi32(0, 0, 0, (uint32_t)h->prim_poly); + + /* we need to test algorithm 2 later*/ + c = _mm_clmulepi64_si128 (a, b, 0x00); /*low-low*/ + f = _mm_clmulepi64_si128 (a, b, 0x01); /*high-low*/ + e = _mm_clmulepi64_si128 (a, b, 0x10); /*low-high*/ + d = _mm_clmulepi64_si128 (a, b, 0x11); /*high-high*/ + + /* now reusing a and b as temporary variables*/ + result0 = _mm_setzero_si128(); + result1 = result0; + + result0 = _mm_xor_si128 (result0, _mm_insert_epi64 (d, 0, 0)); + a = _mm_xor_si128 (_mm_srli_si128 (e, 8), _mm_insert_epi64 (d, 0, 1)); + result0 = _mm_xor_si128 (result0, _mm_xor_si128 (_mm_srli_si128 (f, 8), a)); + + a = _mm_xor_si128 (_mm_slli_si128 (e, 8), _mm_insert_epi64 (c, 0, 0)); + result1 = _mm_xor_si128 (result1, _mm_xor_si128 (_mm_slli_si128 (f, 8), a)); + result1 = _mm_xor_si128 (result1, _mm_insert_epi64 (c, 0, 1)); + /* now we have constructed our 'result' with result0 being the carry bits, and we have to reduce.*/ + + a = _mm_srli_si128 (result0, 8); + b = _mm_clmulepi64_si128 (a, prim_poly, 0x00); + result0 = _mm_xor_si128 (result0, _mm_srli_si128 (b, 8)); + result1 = _mm_xor_si128 (result1, _mm_slli_si128 (b, 8)); + + a = _mm_insert_epi64 (result0, 0, 1); + b = _mm_clmulepi64_si128 (a, prim_poly, 0x00); + result1 = _mm_xor_si128 (result1, b); + + c128[0] = (uint64_t)_mm_extract_epi64(result1,1); + c128[1] = (uint64_t)_mm_extract_epi64(result1,0); +} +#endif + +void +gf_w128_bytwo_p_multiply(gf_t *gf, gf_val_128_t a128, gf_val_128_t b128, gf_val_128_t c128) +{ + uint64_t amask[2], pmask, pp, prod[2]; /*John: pmask is always the highest bit set, and the rest zeros. amask changes, it's a countdown.*/ + uint64_t topbit; /* this is used as a boolean value */ + gf_internal_t *h; + + h = (gf_internal_t *) gf->scratch; + pp = h->prim_poly; + prod[0] = 0; + prod[1] = 0; + pmask = 0x8000000000000000ULL; + amask[0] = 0x8000000000000000ULL; + amask[1] = 0; + + while (amask[1] != 0 || amask[0] != 0) { + topbit = (prod[0] & pmask); + prod[0] <<= 1; + if (prod[1] & pmask) prod[0] ^= 1; + prod[1] <<= 1; + if (topbit) prod[1] ^= pp; + if ((a128[0] & amask[0]) || (a128[1] & amask[1])) { + prod[0] ^= b128[0]; + prod[1] ^= b128[1]; + } + amask[1] >>= 1; + if (amask[0] & 1) amask[1] ^= pmask; + amask[0] >>= 1; + } + c128[0] = prod [0]; + c128[1] = prod [1]; + return; +} + +#if defined(INTEL_SSE4) +void +gf_w128_sse_bytwo_p_multiply(gf_t *gf, gf_val_128_t a128, gf_val_128_t b128, gf_val_128_t c128) +{ + int i; + __m128i a, b, pp, prod, amask, u_middle_one; + /*John: pmask is always the highest bit set, and the rest zeros. amask changes, it's a countdown.*/ + uint32_t topbit, middlebit, pmask; /* this is used as a boolean value */ + gf_internal_t *h; + + + h = (gf_internal_t *) gf->scratch; + pp = _mm_set_epi32(0, 0, 0, (uint32_t)h->prim_poly); + prod = _mm_setzero_si128(); + a = _mm_insert_epi64(prod, a128[1], 0x0); + a = _mm_insert_epi64(a, a128[0], 0x1); + b = _mm_insert_epi64(prod, b128[1], 0x0); + b = _mm_insert_epi64(b, b128[0], 0x1); + pmask = 0x80000000; + amask = _mm_insert_epi32(prod, 0x80000000, 0x3); + u_middle_one = _mm_insert_epi32(prod, 1, 0x2); + + for (i = 0; i < 64; i++) { + topbit = (_mm_extract_epi32(prod, 0x3) & pmask); + middlebit = (_mm_extract_epi32(prod, 0x1) & pmask); + prod = _mm_slli_epi64(prod, 1); /* this instruction loses the middle bit */ + if (middlebit) { + prod = _mm_xor_si128(prod, u_middle_one); + } + if (topbit) { + prod = _mm_xor_si128(prod, pp); + } + if (((uint64_t)_mm_extract_epi64(_mm_and_si128(a, amask), 1))) { + prod = _mm_xor_si128(prod, b); + } + amask = _mm_srli_epi64(amask, 1); /*so does this one, but we can just replace after loop*/ + } + amask = _mm_insert_epi32(amask, (gf_val_32_t)1 << 31, 0x1); + for (i = 64; i < 128; i++) { + topbit = (_mm_extract_epi32(prod, 0x3) & pmask); + middlebit = (_mm_extract_epi32(prod, 0x1) & pmask); + prod = _mm_slli_epi64(prod, 1); + if (middlebit) prod = _mm_xor_si128(prod, u_middle_one); + if (topbit) prod = _mm_xor_si128(prod, pp); + if (((uint64_t)_mm_extract_epi64(_mm_and_si128(a, amask), 0))) { + prod = _mm_xor_si128(prod, b); + } + amask = _mm_srli_epi64(amask, 1); + } + c128[0] = (uint64_t)_mm_extract_epi64(prod, 1); + c128[1] = (uint64_t)_mm_extract_epi64(prod, 0); + return; +} +#endif + + +/* Ben: This slow function implements sse instrutions for bytwo_b because why not */ +#if defined(INTEL_SSE4) +void +gf_w128_sse_bytwo_b_multiply(gf_t *gf, gf_val_128_t a128, gf_val_128_t b128, gf_val_128_t c128) +{ + __m128i a, b, lmask, hmask, pp, c, middle_one; + gf_internal_t *h; + uint64_t topbit, middlebit; + + h = (gf_internal_t *) gf->scratch; + + c = _mm_setzero_si128(); + lmask = _mm_insert_epi64(c, 1ULL << 63, 0); + hmask = _mm_insert_epi64(c, 1ULL << 63, 1); + b = _mm_insert_epi64(c, a128[0], 1); + b = _mm_insert_epi64(b, a128[1], 0); + a = _mm_insert_epi64(c, b128[0], 1); + a = _mm_insert_epi64(a, b128[1], 0); + pp = _mm_insert_epi64(c, h->prim_poly, 0); + middle_one = _mm_insert_epi64(c, 1, 0x1); + + while (1) { + if (_mm_extract_epi32(a, 0x0) & 1) { + c = _mm_xor_si128(c, b); + } + middlebit = (_mm_extract_epi32(a, 0x2) & 1); + a = _mm_srli_epi64(a, 1); + if (middlebit) a = _mm_xor_si128(a, lmask); + if ((_mm_extract_epi64(a, 0x1) == 0ULL) && (_mm_extract_epi64(a, 0x0) == 0ULL)){ + c128[0] = _mm_extract_epi64(c, 0x1); + c128[1] = _mm_extract_epi64(c, 0x0); + return; + } + topbit = (_mm_extract_epi64(_mm_and_si128(b, hmask), 1)); + middlebit = (_mm_extract_epi64(_mm_and_si128(b, lmask), 0)); + b = _mm_slli_epi64(b, 1); + if (middlebit) b = _mm_xor_si128(b, middle_one); + if (topbit) b = _mm_xor_si128(b, pp); + } +} +#endif + +void +gf_w128_bytwo_b_multiply(gf_t *gf, gf_val_128_t a128, gf_val_128_t b128, gf_val_128_t c128) +{ + uint64_t bmask, pp; + gf_internal_t *h; + uint64_t a[2], b[2], c[2]; + + h = (gf_internal_t *) gf->scratch; + + bmask = (1ULL << 63); + set_zero(c, 0); + b[0] = a128[0]; + b[1] = a128[1]; + a[0] = b128[0]; + a[1] = b128[1]; + + while (1) { + if (a[1] & 1) { + c[0] ^= b[0]; + c[1] ^= b[1]; + } + a[1] >>= 1; + if (a[0] & 1) a[1] ^= bmask; + a[0] >>= 1; + if (a[1] == 0 && a[0] == 0) { + c128[0] = c[0]; + c128[1] = c[1]; + return; + } + pp = (b[0] & bmask); + b[0] <<= 1; + if (b[1] & bmask) b[0] ^= 1; + b[1] <<= 1; + if (pp) b[1] ^= h->prim_poly; + } +} + +static +void +gf_w128_split_4_128_multiply_region(gf_t *gf, void *src, void *dest, gf_val_128_t val, int bytes, int xor) +{ + int i, j, k; + uint64_t pp; + gf_internal_t *h; + uint64_t *s64, *d64, *top; + gf_region_data rd; + uint64_t v[2], s; + struct gf_w128_split_4_128_data *ld; + + /* We only do this to check on alignment. */ + gf_set_region_data(&rd, gf, src, dest, bytes, 0, xor, 8); + + if (val[0] == 0) { + if (val[1] == 0) { gf_multby_zero(dest, bytes, xor); return; } + if (val[1] == 1) { gf_multby_one(src, dest, bytes, xor); return; } + } + + h = (gf_internal_t *) gf->scratch; + ld = (struct gf_w128_split_4_128_data *) h->private; + + s64 = (uint64_t *) rd.s_start; + d64 = (uint64_t *) rd.d_start; + top = (uint64_t *) rd.d_top; + + if (val[0] != ld->last_value[0] || val[1] != ld->last_value[1]) { + v[0] = val[0]; + v[1] = val[1]; + for (i = 0; i < 32; i++) { + ld->tables[0][i][0] = 0; + ld->tables[1][i][0] = 0; + for (j = 1; j < 16; j <<= 1) { + for (k = 0; k < j; k++) { + ld->tables[0][i][k^j] = (v[0] ^ ld->tables[0][i][k]); + ld->tables[1][i][k^j] = (v[1] ^ ld->tables[1][i][k]); + } + pp = (v[0] & (1ULL << 63)); + v[0] <<= 1; + if (v[1] & (1ULL << 63)) v[0] ^= 1; + v[1] <<= 1; + if (pp) v[1] ^= h->prim_poly; + } + } + } + ld->last_value[0] = val[0]; + ld->last_value[1] = val[1]; + +/* + for (i = 0; i < 32; i++) { + for (j = 0; j < 16; j++) { + printf("%2d %2d %016llx %016llx\n", i, j, ld->tables[0][i][j], ld->tables[1][i][j]); + } + printf("\n"); + } + */ + while (d64 < top) { + v[0] = (xor) ? d64[0] : 0; + v[1] = (xor) ? d64[1] : 0; + s = s64[1]; + i = 0; + while (s != 0) { + v[0] ^= ld->tables[0][i][s&0xf]; + v[1] ^= ld->tables[1][i][s&0xf]; + s >>= 4; + i++; + } + s = s64[0]; + i = 16; + while (s != 0) { + v[0] ^= ld->tables[0][i][s&0xf]; + v[1] ^= ld->tables[1][i][s&0xf]; + s >>= 4; + i++; + } + d64[0] = v[0]; + d64[1] = v[1]; + s64 += 2; + d64 += 2; + } +} + +#if defined(INTEL_SSSE3) && defined(INTEL_SSE4) +static +void +gf_w128_split_4_128_sse_multiply_region(gf_t *gf, void *src, void *dest, gf_val_128_t val, int bytes, int xor) +{ + gf_internal_t *h; + int i, j, k; + uint64_t pp, v[2], s, *s64, *d64, *top; + __m128i p, tables[32][16]; + struct gf_w128_split_4_128_data *ld; + gf_region_data rd; + + if (val[0] == 0) { + if (val[1] == 0) { gf_multby_zero(dest, bytes, xor); return; } + if (val[1] == 1) { gf_multby_one(src, dest, bytes, xor); return; } + } + + h = (gf_internal_t *) gf->scratch; + + /* We only do this to check on alignment. */ + gf_set_region_data(&rd, gf, src, dest, bytes, 0, xor, 16); + + /* Doing this instead of gf_do_initial_region_alignment() because that doesn't hold 128-bit vals */ + + gf_w128_multiply_region_from_single(gf, src, dest, val, ((uint8_t *)rd.s_start-(uint8_t *)src), xor); + + s64 = (uint64_t *) rd.s_start; + d64 = (uint64_t *) rd.d_start; + top = (uint64_t *) rd.d_top; + + ld = (struct gf_w128_split_4_128_data *) h->private; + + if (val[0] != ld->last_value[0] || val[1] != ld->last_value[1]) { + v[0] = val[0]; + v[1] = val[1]; + for (i = 0; i < 32; i++) { + ld->tables[0][i][0] = 0; + ld->tables[1][i][0] = 0; + for (j = 1; j < 16; j <<= 1) { + for (k = 0; k < j; k++) { + ld->tables[0][i][k^j] = (v[0] ^ ld->tables[0][i][k]); + ld->tables[1][i][k^j] = (v[1] ^ ld->tables[1][i][k]); + } + pp = (v[0] & (1ULL << 63)); + v[0] <<= 1; + if (v[1] & (1ULL << 63)) v[0] ^= 1; + v[1] <<= 1; + if (pp) v[1] ^= h->prim_poly; + } + } + } + + ld->last_value[0] = val[0]; + ld->last_value[1] = val[1]; + + for (i = 0; i < 32; i++) { + for (j = 0; j < 16; j++) { + v[0] = ld->tables[0][i][j]; + v[1] = ld->tables[1][i][j]; + tables[i][j] = _mm_loadu_si128((__m128i *) v); + +/* + printf("%2d %2d: ", i, j); + MM_PRINT8("", tables[i][j]); */ + } + } + + while (d64 != top) { + + if (xor) { + p = _mm_load_si128 ((__m128i *) d64); + } else { + p = _mm_setzero_si128(); + } + s = *s64; + s64++; + for (i = 0; i < 16; i++) { + j = (s&0xf); + s >>= 4; + p = _mm_xor_si128(p, tables[16+i][j]); + } + s = *s64; + s64++; + for (i = 0; i < 16; i++) { + j = (s&0xf); + s >>= 4; + p = _mm_xor_si128(p, tables[i][j]); + } + _mm_store_si128((__m128i *) d64, p); + d64 += 2; + } + + /* Doing this instead of gf_do_final_region_alignment() because that doesn't hold 128-bit vals */ + + gf_w128_multiply_region_from_single(gf, rd.s_top, rd.d_top, val, ((uint8_t *)src+bytes)-(uint8_t *)rd.s_top, xor); +} +#endif + +#if defined(INTEL_SSSE3) && defined(INTEL_SSE4) +static +void +gf_w128_split_4_128_sse_altmap_multiply_region(gf_t *gf, void *src, void *dest, gf_val_128_t val, int bytes, int xor) +{ + gf_internal_t *h; + int i, j, k; + uint64_t pp, v[2], *s64, *d64, *top; + __m128i si, tables[32][16], p[16], v0, mask1; + struct gf_w128_split_4_128_data *ld; + uint8_t btable[16]; + gf_region_data rd; + + if (val[0] == 0) { + if (val[1] == 0) { gf_multby_zero(dest, bytes, xor); return; } + if (val[1] == 1) { gf_multby_one(src, dest, bytes, xor); return; } + } + + h = (gf_internal_t *) gf->scratch; + + /* We only do this to check on alignment. */ + gf_set_region_data(&rd, gf, src, dest, bytes, 0, xor, 256); + + /* Doing this instead of gf_do_initial_region_alignment() because that doesn't hold 128-bit vals */ + + gf_w128_multiply_region_from_single(gf, src, dest, val, ((uint8_t *)rd.s_start-(uint8_t *)src), xor); + + s64 = (uint64_t *) rd.s_start; + d64 = (uint64_t *) rd.d_start; + top = (uint64_t *) rd.d_top; + + ld = (struct gf_w128_split_4_128_data *) h->private; + + if (val[0] != ld->last_value[0] || val[1] != ld->last_value[1]) { + v[0] = val[0]; + v[1] = val[1]; + for (i = 0; i < 32; i++) { + ld->tables[0][i][0] = 0; + ld->tables[1][i][0] = 0; + for (j = 1; j < 16; j <<= 1) { + for (k = 0; k < j; k++) { + ld->tables[0][i][k^j] = (v[0] ^ ld->tables[0][i][k]); + ld->tables[1][i][k^j] = (v[1] ^ ld->tables[1][i][k]); + } + pp = (v[0] & (1ULL << 63)); + v[0] <<= 1; + if (v[1] & (1ULL << 63)) v[0] ^= 1; + v[1] <<= 1; + if (pp) v[1] ^= h->prim_poly; + } + } + } + + ld->last_value[0] = val[0]; + ld->last_value[1] = val[1]; + + for (i = 0; i < 32; i++) { + for (j = 0; j < 16; j++) { + for (k = 0; k < 16; k++) { + btable[k] = (uint8_t) ld->tables[1-(j/8)][i][k]; + ld->tables[1-(j/8)][i][k] >>= 8; + } + tables[i][j] = _mm_loadu_si128((__m128i *) btable); +/* + printf("%2d %2d: ", i, j); + MM_PRINT8("", tables[i][j]); + */ + } + } + + + mask1 = _mm_set1_epi8(0xf); + + while (d64 != top) { + + if (xor) { + for (i = 0; i < 16; i++) p[i] = _mm_load_si128 ((__m128i *) (d64+i*2)); + } else { + for (i = 0; i < 16; i++) p[i] = _mm_setzero_si128(); + } + i = 0; + for (k = 0; k < 16; k++) { + v0 = _mm_load_si128((__m128i *) s64); + s64 += 2; + + si = _mm_and_si128(v0, mask1); + + for (j = 0; j < 16; j++) { + p[j] = _mm_xor_si128(p[j], _mm_shuffle_epi8(tables[i][j], si)); + } + i++; + v0 = _mm_srli_epi32(v0, 4); + si = _mm_and_si128(v0, mask1); + for (j = 0; j < 16; j++) { + p[j] = _mm_xor_si128(p[j], _mm_shuffle_epi8(tables[i][j], si)); + } + i++; + } + for (i = 0; i < 16; i++) { + _mm_store_si128((__m128i *) d64, p[i]); + d64 += 2; + } + } + /* Doing this instead of gf_do_final_region_alignment() because that doesn't hold 128-bit vals */ + + gf_w128_multiply_region_from_single(gf, rd.s_top, rd.d_top, val, ((uint8_t *)src+bytes)-(uint8_t *)rd.s_top, xor); +} +#endif + +static +void +gf_w128_split_8_128_multiply_region(gf_t *gf, void *src, void *dest, gf_val_128_t val, int bytes, int xor) +{ + int i, j, k; + uint64_t pp; + gf_internal_t *h; + uint64_t *s64, *d64, *top; + gf_region_data rd; + uint64_t v[2], s; + struct gf_w128_split_8_128_data *ld; + + /* Check on alignment. Ignore it otherwise. */ + gf_set_region_data(&rd, gf, src, dest, bytes, 0, xor, 8); + + if (val[0] == 0) { + if (val[1] == 0) { gf_multby_zero(dest, bytes, xor); return; } + if (val[1] == 1) { gf_multby_one(src, dest, bytes, xor); return; } + } + + h = (gf_internal_t *) gf->scratch; + ld = (struct gf_w128_split_8_128_data *) h->private; + + s64 = (uint64_t *) rd.s_start; + d64 = (uint64_t *) rd.d_start; + top = (uint64_t *) rd.d_top; + + if (val[0] != ld->last_value[0] || val[1] != ld->last_value[1]) { + v[0] = val[0]; + v[1] = val[1]; + for (i = 0; i < 16; i++) { + ld->tables[0][i][0] = 0; + ld->tables[1][i][0] = 0; + for (j = 1; j < (1 << 8); j <<= 1) { + for (k = 0; k < j; k++) { + ld->tables[0][i][k^j] = (v[0] ^ ld->tables[0][i][k]); + ld->tables[1][i][k^j] = (v[1] ^ ld->tables[1][i][k]); + } + pp = (v[0] & (1ULL << 63)); + v[0] <<= 1; + if (v[1] & (1ULL << 63)) v[0] ^= 1; + v[1] <<= 1; + if (pp) v[1] ^= h->prim_poly; + } + } + } + ld->last_value[0] = val[0]; + ld->last_value[1] = val[1]; + + while (d64 < top) { + v[0] = (xor) ? d64[0] : 0; + v[1] = (xor) ? d64[1] : 0; + s = s64[1]; + i = 0; + while (s != 0) { + v[0] ^= ld->tables[0][i][s&0xff]; + v[1] ^= ld->tables[1][i][s&0xff]; + s >>= 8; + i++; + } + s = s64[0]; + i = 8; + while (s != 0) { + v[0] ^= ld->tables[0][i][s&0xff]; + v[1] ^= ld->tables[1][i][s&0xff]; + s >>= 8; + i++; + } + d64[0] = v[0]; + d64[1] = v[1]; + s64 += 2; + d64 += 2; + } +} + +void +gf_w128_bytwo_b_multiply_region(gf_t *gf, void *src, void *dest, gf_val_128_t val, int bytes, int xor) +{ + uint64_t bmask, pp; + gf_internal_t *h; + uint64_t a[2], c[2], b[2], *s64, *d64, *top; + gf_region_data rd; + + /* We only do this to check on alignment. */ + gf_set_region_data(&rd, gf, src, dest, bytes, 0, xor, 8); + + if (val[0] == 0) { + if (val[1] == 0) { gf_multby_zero(dest, bytes, xor); return; } + if (val[1] == 1) { gf_multby_one(src, dest, bytes, xor); return; } + } + + h = (gf_internal_t *) gf->scratch; + s64 = (uint64_t *) rd.s_start; + d64 = (uint64_t *) rd.d_start; + top = (uint64_t *) rd.d_top; + bmask = (1ULL << 63); + + while (d64 < top) { + set_zero(c, 0); + b[0] = s64[0]; + b[1] = s64[1]; + a[0] = val[0]; + a[1] = val[1]; + + while (a[0] != 0) { + if (a[1] & 1) { + c[0] ^= b[0]; + c[1] ^= b[1]; + } + a[1] >>= 1; + if (a[0] & 1) a[1] ^= bmask; + a[0] >>= 1; + pp = (b[0] & bmask); + b[0] <<= 1; + if (b[1] & bmask) b[0] ^= 1; + b[1] <<= 1; + if (pp) b[1] ^= h->prim_poly; + } + while (1) { + if (a[1] & 1) { + c[0] ^= b[0]; + c[1] ^= b[1]; + } + a[1] >>= 1; + if (a[1] == 0) break; + pp = (b[0] & bmask); + b[0] <<= 1; + if (b[1] & bmask) b[0] ^= 1; + b[1] <<= 1; + if (pp) b[1] ^= h->prim_poly; + } + if (xor) { + d64[0] ^= c[0]; + d64[1] ^= c[1]; + } else { + d64[0] = c[0]; + d64[1] = c[1]; + } + s64 += 2; + d64 += 2; + } +} + +static +void gf_w128_group_m_init(gf_t *gf, gf_val_128_t b128) +{ + int i, j; + int g_m; + uint64_t prim_poly, lbit; + gf_internal_t *scratch; + gf_group_tables_t *gt; + uint64_t a128[2]; + scratch = (gf_internal_t *) gf->scratch; + gt = scratch->private; + g_m = scratch->arg1; + prim_poly = scratch->prim_poly; + + + set_zero(gt->m_table, 0); + a_get_b(gt->m_table, 2, b128, 0); + lbit = 1; + lbit <<= 63; + + for (i = 2; i < (1 << g_m); i <<= 1) { + a_get_b(a128, 0, gt->m_table, 2 * (i >> 1)); + two_x(a128); + a_get_b(gt->m_table, 2 * i, a128, 0); + if (gt->m_table[2 * (i >> 1)] & lbit) gt->m_table[(2 * i) + 1] ^= prim_poly; + for (j = 0; j < i; j++) { + gt->m_table[(2 * i) + (2 * j)] = gt->m_table[(2 * i)] ^ gt->m_table[(2 * j)]; + gt->m_table[(2 * i) + (2 * j) + 1] = gt->m_table[(2 * i) + 1] ^ gt->m_table[(2 * j) + 1]; + } + } + return; +} + +void +gf_w128_group_multiply(GFP gf, gf_val_128_t a128, gf_val_128_t b128, gf_val_128_t c128) +{ + int i; + /* index_r, index_m, total_m (if g_r > g_m) */ + int i_r, i_m, t_m; + int mask_m, mask_r; + int g_m, g_r; + uint64_t p_i[2], a[2]; + gf_internal_t *scratch; + gf_group_tables_t *gt; + + scratch = (gf_internal_t *) gf->scratch; + gt = scratch->private; + g_m = scratch->arg1; + g_r = scratch->arg2; + + mask_m = (1 << g_m) - 1; + mask_r = (1 << g_r) - 1; + + if (b128[0] != gt->m_table[2] || b128[1] != gt->m_table[3]) { + gf_w128_group_m_init(gf, b128); + } + + p_i[0] = 0; + p_i[1] = 0; + a[0] = a128[0]; + a[1] = a128[1]; + + t_m = 0; + i_r = 0; + + /* Top 64 bits */ + for (i = ((GF_FIELD_WIDTH / 2) / g_m) - 1; i >= 0; i--) { + i_m = (a[0] >> (i * g_m)) & mask_m; + i_r ^= (p_i[0] >> (64 - g_m)) & mask_r; + p_i[0] <<= g_m; + p_i[0] ^= (p_i[1] >> (64-g_m)); + p_i[1] <<= g_m; + p_i[0] ^= gt->m_table[2 * i_m]; + p_i[1] ^= gt->m_table[(2 * i_m) + 1]; + t_m += g_m; + if (t_m == g_r) { + p_i[1] ^= gt->r_table[i_r]; + t_m = 0; + i_r = 0; + } else { + i_r <<= g_m; + } + } + + for (i = ((GF_FIELD_WIDTH / 2) / g_m) - 1; i >= 0; i--) { + i_m = (a[1] >> (i * g_m)) & mask_m; + i_r ^= (p_i[0] >> (64 - g_m)) & mask_r; + p_i[0] <<= g_m; + p_i[0] ^= (p_i[1] >> (64-g_m)); + p_i[1] <<= g_m; + p_i[0] ^= gt->m_table[2 * i_m]; + p_i[1] ^= gt->m_table[(2 * i_m) + 1]; + t_m += g_m; + if (t_m == g_r) { + p_i[1] ^= gt->r_table[i_r]; + t_m = 0; + i_r = 0; + } else { + i_r <<= g_m; + } + } + c128[0] = p_i[0]; + c128[1] = p_i[1]; +} + +static +void +gf_w128_group_multiply_region(gf_t *gf, void *src, void *dest, gf_val_128_t val, int bytes, int xor) +{ + int i; + int i_r, i_m, t_m; + int mask_m, mask_r; + int g_m, g_r; + uint64_t p_i[2], a[2]; + gf_internal_t *scratch; + gf_group_tables_t *gt; + gf_region_data rd; + uint64_t *a128, *c128, *top; + + /* We only do this to check on alignment. */ + gf_set_region_data(&rd, gf, src, dest, bytes, 0, xor, 8); + + if (val[0] == 0) { + if (val[1] == 0) { gf_multby_zero(dest, bytes, xor); return; } + if (val[1] == 1) { gf_multby_one(src, dest, bytes, xor); return; } + } + + scratch = (gf_internal_t *) gf->scratch; + gt = scratch->private; + g_m = scratch->arg1; + g_r = scratch->arg2; + + mask_m = (1 << g_m) - 1; + mask_r = (1 << g_r) - 1; + + if (val[0] != gt->m_table[2] || val[1] != gt->m_table[3]) { + gf_w128_group_m_init(gf, val); + } + + a128 = (uint64_t *) src; + c128 = (uint64_t *) dest; + top = (uint64_t *) rd.d_top; + + while (c128 < top) { + p_i[0] = 0; + p_i[1] = 0; + a[0] = a128[0]; + a[1] = a128[1]; + + t_m = 0; + i_r = 0; + + /* Top 64 bits */ + for (i = ((GF_FIELD_WIDTH / 2) / g_m) - 1; i >= 0; i--) { + i_m = (a[0] >> (i * g_m)) & mask_m; + i_r ^= (p_i[0] >> (64 - g_m)) & mask_r; + p_i[0] <<= g_m; + p_i[0] ^= (p_i[1] >> (64-g_m)); + p_i[1] <<= g_m; + + p_i[0] ^= gt->m_table[2 * i_m]; + p_i[1] ^= gt->m_table[(2 * i_m) + 1]; + t_m += g_m; + if (t_m == g_r) { + p_i[1] ^= gt->r_table[i_r]; + t_m = 0; + i_r = 0; + } else { + i_r <<= g_m; + } + } + for (i = ((GF_FIELD_WIDTH / 2) / g_m) - 1; i >= 0; i--) { + i_m = (a[1] >> (i * g_m)) & mask_m; + i_r ^= (p_i[0] >> (64 - g_m)) & mask_r; + p_i[0] <<= g_m; + p_i[0] ^= (p_i[1] >> (64-g_m)); + p_i[1] <<= g_m; + p_i[0] ^= gt->m_table[2 * i_m]; + p_i[1] ^= gt->m_table[(2 * i_m) + 1]; + t_m += g_m; + if (t_m == g_r) { + p_i[1] ^= gt->r_table[i_r]; + t_m = 0; + i_r = 0; + } else { + i_r <<= g_m; + } + } + + if (xor) { + c128[0] ^= p_i[0]; + c128[1] ^= p_i[1]; + } else { + c128[0] = p_i[0]; + c128[1] = p_i[1]; + } + a128 += 2; + c128 += 2; + } +} + +/* a^-1 -> b */ +void +gf_w128_euclid(GFP gf, gf_val_128_t a128, gf_val_128_t b128) +{ + uint64_t e_i[2], e_im1[2], e_ip1[2]; + uint64_t d_i, d_im1, d_ip1; + uint64_t y_i[2], y_im1[2], y_ip1[2]; + uint64_t c_i[2]; + uint64_t *b; + uint64_t one = 1; + + /* This needs to return some sort of error (in b128?) */ + if (a128[0] == 0 && a128[1] == 0) return; + + b = (uint64_t *) b128; + + e_im1[0] = 0; + e_im1[1] = ((gf_internal_t *) (gf->scratch))->prim_poly; + e_i[0] = a128[0]; + e_i[1] = a128[1]; + d_im1 = 128; + + //Allen: I think d_i starts at 63 here, and checks each bit of a, starting at MSB, looking for the first nonzero bit + //so d_i should be 0 if this half of a is all 0s, otherwise it should be the position from right of the first-from-left zero bit of this half of a. + //BUT if d_i is 0 at end we won't know yet if the rightmost bit of this half is 1 or not + + for (d_i = (d_im1-1) % 64; ((one << d_i) & e_i[0]) == 0 && d_i > 0; d_i--) ; + + //Allen: this is testing just the first half of the stop condition above, so if it holds we know we did not find a nonzero bit yet + + if (!((one << d_i) & e_i[0])) { + + //Allen: this is doing the same thing on the other half of a. In other words, we're still searching for a nonzero bit of a. + // but not bothering to test if d_i hits zero, which is fine because we've already tested for a=0. + + for (d_i = (d_im1-1) % 64; ((one << d_i) & e_i[1]) == 0; d_i--) ; + + } else { + + //Allen: if a 1 was found in more-significant half of a, make d_i the ACTUAL index of the first nonzero bit in the entire a. + + d_i += 64; + } + y_i[0] = 0; + y_i[1] = 1; + y_im1[0] = 0; + y_im1[1] = 0; + + while (!(e_i[0] == 0 && e_i[1] == 1)) { + + e_ip1[0] = e_im1[0]; + e_ip1[1] = e_im1[1]; + d_ip1 = d_im1; + c_i[0] = 0; + c_i[1] = 0; + + while (d_ip1 >= d_i) { + if ((d_ip1 - d_i) >= 64) { + c_i[0] ^= (one << ((d_ip1 - d_i) - 64)); + e_ip1[0] ^= (e_i[1] << ((d_ip1 - d_i) - 64)); + } else { + c_i[1] ^= (one << (d_ip1 - d_i)); + e_ip1[0] ^= (e_i[0] << (d_ip1 - d_i)); + if (d_ip1 - d_i > 0) e_ip1[0] ^= (e_i[1] >> (64 - (d_ip1 - d_i))); + e_ip1[1] ^= (e_i[1] << (d_ip1 - d_i)); + } + d_ip1--; + if (e_ip1[0] == 0 && e_ip1[1] == 0) { b[0] = 0; b[1] = 0; return; } + while (d_ip1 >= 64 && (e_ip1[0] & (one << (d_ip1 - 64))) == 0) d_ip1--; + while (d_ip1 < 64 && (e_ip1[1] & (one << d_ip1)) == 0) d_ip1--; + } + gf->multiply.w128(gf, c_i, y_i, y_ip1); + y_ip1[0] ^= y_im1[0]; + y_ip1[1] ^= y_im1[1]; + + y_im1[0] = y_i[0]; + y_im1[1] = y_i[1]; + + y_i[0] = y_ip1[0]; + y_i[1] = y_ip1[1]; + + e_im1[0] = e_i[0]; + e_im1[1] = e_i[1]; + d_im1 = d_i; + e_i[0] = e_ip1[0]; + e_i[1] = e_ip1[1]; + d_i = d_ip1; + } + + b[0] = y_i[0]; + b[1] = y_i[1]; + return; +} + +void +gf_w128_divide_from_inverse(GFP gf, gf_val_128_t a128, gf_val_128_t b128, gf_val_128_t c128) +{ + uint64_t d[2]; + gf->inverse.w128(gf, b128, d); + gf->multiply.w128(gf, a128, d, c128); + return; +} + +void +gf_w128_inverse_from_divide(GFP gf, gf_val_128_t a128, gf_val_128_t b128) +{ + uint64_t one128[2]; + one128[0] = 0; + one128[1] = 1; + gf->divide.w128(gf, one128, a128, b128); + return; +} + + +static +void +gf_w128_composite_inverse(gf_t *gf, gf_val_128_t a, gf_val_128_t inv) +{ + gf_internal_t *h = (gf_internal_t *) gf->scratch; + gf_t *base_gf = h->base_gf; + uint64_t a0 = a[1]; + uint64_t a1 = a[0]; + uint64_t c0, c1, d, tmp; + uint64_t a0inv, a1inv; + + if (a0 == 0) { + a1inv = base_gf->inverse.w64(base_gf, a1); + c0 = base_gf->multiply.w64(base_gf, a1inv, h->prim_poly); + c1 = a1inv; + } else if (a1 == 0) { + c0 = base_gf->inverse.w64(base_gf, a0); + c1 = 0; + } else { + a1inv = base_gf->inverse.w64(base_gf, a1); + a0inv = base_gf->inverse.w64(base_gf, a0); + + d = base_gf->multiply.w64(base_gf, a1, a0inv); + + tmp = (base_gf->multiply.w64(base_gf, a1, a0inv) ^ base_gf->multiply.w64(base_gf, a0, a1inv) ^ h->prim_poly); + tmp = base_gf->inverse.w64(base_gf, tmp); + + d = base_gf->multiply.w64(base_gf, d, tmp); + + c0 = base_gf->multiply.w64(base_gf, (d^1), a0inv); + c1 = base_gf->multiply.w64(base_gf, d, a1inv); + } + inv[0] = c1; + inv[1] = c0; +} + +static + void +gf_w128_composite_multiply(gf_t *gf, gf_val_128_t a, gf_val_128_t b, gf_val_128_t rv) +{ + gf_internal_t *h = (gf_internal_t *) gf->scratch; + gf_t *base_gf = h->base_gf; + uint64_t b0 = b[1]; + uint64_t b1 = b[0]; + uint64_t a0 = a[1]; + uint64_t a1 = a[0]; + uint64_t a1b1; + + a1b1 = base_gf->multiply.w64(base_gf, a1, b1); + + rv[1] = (base_gf->multiply.w64(base_gf, a0, b0) ^ a1b1); + rv[0] = base_gf->multiply.w64(base_gf, a1, b0) ^ + base_gf->multiply.w64(base_gf, a0, b1) ^ + base_gf->multiply.w64(base_gf, a1b1, h->prim_poly); +} + +static + void +gf_w128_composite_multiply_region(gf_t *gf, void *src, void *dest, gf_val_128_t val, int bytes, int xor) +{ + gf_internal_t *h = (gf_internal_t *) gf->scratch; + gf_t *base_gf = h->base_gf; + uint64_t b0 = val[1]; + uint64_t b1 = val[0]; + uint64_t *s64, *d64; + uint64_t *top; + uint64_t a0, a1, a1b1; + gf_region_data rd; + + if (val[0] == 0 && val[1] == 0) { gf_multby_zero(dest, bytes, xor); return; } + + gf_set_region_data(&rd, gf, src, dest, bytes, 0, xor, 8); + + s64 = rd.s_start; + d64 = rd.d_start; + top = rd.d_top; + + if (xor) { + while (d64 < top) { + a1 = s64[0]; + a0 = s64[1]; + a1b1 = base_gf->multiply.w64(base_gf, a1, b1); + + d64[1] ^= (base_gf->multiply.w64(base_gf, a0, b0) ^ a1b1); + d64[0] ^= (base_gf->multiply.w64(base_gf, a1, b0) ^ + base_gf->multiply.w64(base_gf, a0, b1) ^ + base_gf->multiply.w64(base_gf, a1b1, h->prim_poly)); + s64 += 2; + d64 += 2; + } + } else { + while (d64 < top) { + a1 = s64[0]; + a0 = s64[1]; + a1b1 = base_gf->multiply.w64(base_gf, a1, b1); + + d64[1] = (base_gf->multiply.w64(base_gf, a0, b0) ^ a1b1); + d64[0] = (base_gf->multiply.w64(base_gf, a1, b0) ^ + base_gf->multiply.w64(base_gf, a0, b1) ^ + base_gf->multiply.w64(base_gf, a1b1, h->prim_poly)); + s64 += 2; + d64 += 2; + } + } +} + +static +void +gf_w128_composite_multiply_region_alt(gf_t *gf, void *src, void *dest, gf_val_128_t val, int bytes, int + xor) +{ + gf_internal_t *h = (gf_internal_t *) gf->scratch; gf_t *base_gf = h->base_gf; + gf_val_64_t val0 = val[1]; + gf_val_64_t val1 = val[0]; + uint8_t *slow, *shigh; + uint8_t *dlow, *dhigh, *top; + int sub_reg_size; + gf_region_data rd; + + gf_set_region_data(&rd, gf, src, dest, bytes, 0, xor, 64); + gf_w128_multiply_region_from_single(gf, src, dest, val, ((uint8_t *)rd.s_start-(uint8_t *)src), xor); + + slow = (uint8_t *) rd.s_start; + dlow = (uint8_t *) rd.d_start; + top = (uint8_t*) rd.d_top; + sub_reg_size = (top - dlow)/2; + shigh = slow + sub_reg_size; + dhigh = dlow + sub_reg_size; + + base_gf->multiply_region.w64(base_gf, slow, dlow, val0, sub_reg_size, xor); + base_gf->multiply_region.w64(base_gf, shigh, dlow, val1, sub_reg_size, 1); + base_gf->multiply_region.w64(base_gf, slow, dhigh, val1, sub_reg_size, xor); + base_gf->multiply_region.w64(base_gf, shigh, dhigh, val0, sub_reg_size, 1); + base_gf->multiply_region.w64(base_gf, shigh, dhigh, base_gf->multiply.w64(base_gf, h->prim_poly, val1 + ), sub_reg_size, 1); + + gf_w128_multiply_region_from_single(gf, rd.s_top, rd.d_top, val, ((uint8_t *)src+bytes)-(uint8_t *)rd.s_top, xor); +} + + + static +int gf_w128_composite_init(gf_t *gf) +{ + gf_internal_t *h = (gf_internal_t *) gf->scratch; + + if (h->region_type & GF_REGION_ALTMAP) { + SET_FUNCTION(gf,multiply_region,w128,gf_w128_composite_multiply_region_alt) + } else { + SET_FUNCTION(gf,multiply_region,w128,gf_w128_composite_multiply_region) + } + + SET_FUNCTION(gf,multiply,w128,gf_w128_composite_multiply) + SET_FUNCTION(gf,divide,w128,gf_w128_divide_from_inverse) + SET_FUNCTION(gf,inverse,w128,gf_w128_composite_inverse) + + return 1; +} + +static +int gf_w128_cfm_init(gf_t *gf) +{ +#if defined(INTEL_SSE4_PCLMUL) + if (gf_cpu_supports_intel_pclmul) { + SET_FUNCTION(gf,inverse,w128,gf_w128_euclid) + SET_FUNCTION(gf,multiply,w128,gf_w128_clm_multiply) + SET_FUNCTION(gf,multiply_region,w128,gf_w128_clm_multiply_region_from_single) + return 1; + } +#endif + + return 0; +} + +static +int gf_w128_shift_init(gf_t *gf) +{ + SET_FUNCTION(gf,multiply,w128,gf_w128_shift_multiply) + SET_FUNCTION(gf,inverse,w128,gf_w128_euclid) + SET_FUNCTION(gf,multiply_region,w128,gf_w128_multiply_region_from_single) + return 1; +} + + static +int gf_w128_bytwo_init(gf_t *gf) +{ + gf_internal_t *h; + h = (gf_internal_t *) gf->scratch; + + if (h->mult_type == GF_MULT_BYTWO_p) { + SET_FUNCTION(gf,multiply,w128,gf_w128_bytwo_p_multiply) + /*SET_FUNCTION(gf,multiply,w128,gf_w128_sse_bytwo_p_multiply)*/ + /* John: the sse function is slower.*/ + } else { + SET_FUNCTION(gf,multiply,w128,gf_w128_bytwo_b_multiply) + /*SET_FUNCTION(gf,multiply,w128,gf_w128_sse_bytwo_b_multiply) +Ben: This sse function is also slower. */ + } + SET_FUNCTION(gf,inverse,w128,gf_w128_euclid) + SET_FUNCTION(gf,multiply_region,w128,gf_w128_bytwo_b_multiply_region) + return 1; +} + +/* + * Because the prim poly is only 8 bits and we are limiting g_r to 16, I do not need the high 64 + * bits in all of these numbers. + */ + static +void gf_w128_group_r_init(gf_t *gf) +{ + int i, j; + int g_r; + uint64_t pp; + gf_internal_t *scratch; + gf_group_tables_t *gt; + scratch = (gf_internal_t *) gf->scratch; + gt = scratch->private; + g_r = scratch->arg2; + pp = scratch->prim_poly; + + gt->r_table[0] = 0; + for (i = 1; i < (1 << g_r); i++) { + gt->r_table[i] = 0; + for (j = 0; j < g_r; j++) { + if (i & (1 << j)) { + gt->r_table[i] ^= (pp << j); + } + } + } + return; +} + +#if 0 // defined(INTEL_SSE4) + static +void gf_w128_group_r_sse_init(gf_t *gf) +{ + int i, j; + int g_r; + uint64_t pp; + gf_internal_t *scratch; + gf_group_tables_t *gt; + scratch = (gf_internal_t *) gf->scratch; + gt = scratch->private; + __m128i zero = _mm_setzero_si128(); + __m128i *table = (__m128i *)(gt->r_table); + g_r = scratch->arg2; + pp = scratch->prim_poly; + table[0] = zero; + for (i = 1; i < (1 << g_r); i++) { + table[i] = zero; + for (j = 0; j < g_r; j++) { + if (i & (1 << j)) { + table[i] = _mm_xor_si128(table[i], _mm_insert_epi64(zero, pp << j, 0)); + } + } + } + return; +} +#endif + + static +int gf_w128_split_init(gf_t *gf) +{ + struct gf_w128_split_4_128_data *sd4; + struct gf_w128_split_8_128_data *sd8; + gf_internal_t *h; + + h = (gf_internal_t *) gf->scratch; + + SET_FUNCTION(gf,multiply,w128,gf_w128_bytwo_p_multiply) +#if defined(INTEL_SSE4_PCLMUL) + if (gf_cpu_supports_intel_pclmul && !(h->region_type & GF_REGION_NOSIMD)){ + SET_FUNCTION(gf,multiply,w128,gf_w128_clm_multiply) + } +#endif + + SET_FUNCTION(gf,inverse,w128,gf_w128_euclid) + + if ((h->arg1 != 4 && h->arg2 != 4) || h->mult_type == GF_MULT_DEFAULT) { + sd8 = (struct gf_w128_split_8_128_data *) h->private; + sd8->last_value[0] = 0; + sd8->last_value[1] = 0; + SET_FUNCTION(gf,multiply_region,w128,gf_w128_split_8_128_multiply_region) + } else { + sd4 = (struct gf_w128_split_4_128_data *) h->private; + sd4->last_value[0] = 0; + sd4->last_value[1] = 0; + if((h->region_type & GF_REGION_ALTMAP)) + { + #ifdef INTEL_SSE4 + if(gf_cpu_supports_intel_sse4 && !(h->region_type & GF_REGION_NOSIMD)) + SET_FUNCTION(gf,multiply_region,w128,gf_w128_split_4_128_sse_altmap_multiply_region) + else + #endif + return 0; + } + else { + #ifdef INTEL_SSE4 + if(gf_cpu_supports_intel_sse4 && !(h->region_type & GF_REGION_NOSIMD)) + SET_FUNCTION(gf,multiply_region,w128,gf_w128_split_4_128_sse_multiply_region) + else + #endif + SET_FUNCTION(gf,multiply_region,w128,gf_w128_split_4_128_multiply_region) + } + } + return 1; +} + + +static +int gf_w128_group_init(gf_t *gf) +{ + gf_internal_t *scratch; + gf_group_tables_t *gt; + int g_r, size_r; + + scratch = (gf_internal_t *) gf->scratch; + gt = scratch->private; + g_r = scratch->arg2; + size_r = (1 << g_r); + + gt->r_table = (gf_val_128_t)((uint8_t *)scratch->private + (2 * sizeof(uint64_t *))); + gt->m_table = gt->r_table + size_r; + gt->m_table[2] = 0; + gt->m_table[3] = 0; + + SET_FUNCTION(gf,multiply,w128,gf_w128_group_multiply) + SET_FUNCTION(gf,inverse,w128,gf_w128_euclid) + SET_FUNCTION(gf,multiply_region,w128,gf_w128_group_multiply_region) + + gf_w128_group_r_init(gf); + + return 1; +} + +void gf_w128_extract_word(gf_t *gf, void *start, int bytes, int index, gf_val_128_t rv) +{ + gf_val_128_t s; + + s = (gf_val_128_t) start; + s += (index * 2); + memcpy(rv, s, 16); +} + +static void gf_w128_split_extract_word(gf_t *gf, void *start, int bytes, int index, gf_val_128_t rv) +{ + int i, blocks; + uint64_t *r64, tmp; + uint8_t *r8; + gf_region_data rd; + + gf_set_region_data(&rd, gf, start, start, bytes, 0, 0, 256); + r64 = (uint64_t *) start; + if ((r64 + index*2 < (uint64_t *) rd.d_start) || + (r64 + index*2 >= (uint64_t *) rd.d_top)) { + memcpy(rv, r64+(index*2), 16); + return; + } + + index -= (((uint64_t *) rd.d_start) - r64)/2; + r64 = (uint64_t *) rd.d_start; + + blocks = index/16; + r64 += (blocks*32); + index %= 16; + r8 = (uint8_t *) r64; + r8 += index; + rv[0] = 0; + rv[1] = 0; + + for (i = 0; i < 8; i++) { + tmp = *r8; + rv[1] |= (tmp << (i*8)); + r8 += 16; + } + + for (i = 0; i < 8; i++) { + tmp = *r8; + rv[0] |= (tmp << (i*8)); + r8 += 16; + } + return; +} + + static +void gf_w128_composite_extract_word(gf_t *gf, void *start, int bytes, int index, gf_val_128_t rv) +{ + int sub_size; + gf_internal_t *h; + uint8_t *r8, *top; + uint64_t *r64; + gf_region_data rd; + + h = (gf_internal_t *) gf->scratch; + gf_set_region_data(&rd, gf, start, start, bytes, 0, 0, 64); + r64 = (uint64_t *) start; + if ((r64 + index*2 < (uint64_t *) rd.d_start) || + (r64 + index*2 >= (uint64_t *) rd.d_top)) { + memcpy(rv, r64+(index*2), 16); + return; + } + index -= (((uint64_t *) rd.d_start) - r64)/2; + r8 = (uint8_t *) rd.d_start; + top = (uint8_t *) rd.d_top; + sub_size = (top-r8)/2; + + rv[1] = h->base_gf->extract_word.w64(h->base_gf, r8, sub_size, index); + rv[0] = h->base_gf->extract_word.w64(h->base_gf, r8+sub_size, sub_size, index); + + return; +} + +int gf_w128_scratch_size(int mult_type, int region_type, int divide_type, int arg1, int arg2) +{ + int size_m, size_r; + if (divide_type==GF_DIVIDE_MATRIX) return 0; + + switch(mult_type) + { + case GF_MULT_CARRY_FREE: + return sizeof(gf_internal_t); + break; + case GF_MULT_SHIFT: + return sizeof(gf_internal_t); + break; + case GF_MULT_BYTWO_p: + case GF_MULT_BYTWO_b: + return sizeof(gf_internal_t); + break; + case GF_MULT_DEFAULT: + case GF_MULT_SPLIT_TABLE: + if ((arg1 == 4 && arg2 == 128) || (arg1 == 128 && arg2 == 4)) { + return sizeof(gf_internal_t) + sizeof(struct gf_w128_split_4_128_data) + 64; + } else if ((arg1 == 8 && arg2 == 128) || (arg1 == 128 && arg2 == 8) || mult_type == GF_MULT_DEFAULT) { + return sizeof(gf_internal_t) + sizeof(struct gf_w128_split_8_128_data) + 64; + } + return 0; + break; + case GF_MULT_GROUP: + /* JSP We've already error checked the arguments. */ + size_m = (1 << arg1) * 2 * sizeof(uint64_t); + size_r = (1 << arg2) * 2 * sizeof(uint64_t); + /* + * two pointers prepend the table data for structure + * because the tables are of dynamic size + */ + return sizeof(gf_internal_t) + size_m + size_r + 4 * sizeof(uint64_t *); + break; + case GF_MULT_COMPOSITE: + if (arg1 == 2) { + return sizeof(gf_internal_t) + 4; + } else { + return 0; + } + break; + + default: + return 0; + } +} + +int gf_w128_init(gf_t *gf) +{ + gf_internal_t *h; + + h = (gf_internal_t *) gf->scratch; + + /* Allen: set default primitive polynomial / irreducible polynomial if needed */ + + if (h->prim_poly == 0) { + if (h->mult_type == GF_MULT_COMPOSITE) { + h->prim_poly = gf_composite_get_default_poly(h->base_gf); + if (h->prim_poly == 0) return 0; /* This shouldn't happen */ + } else { + h->prim_poly = 0x87; /* Omitting the leftmost 1 as in w=32 */ + } + } + + SET_FUNCTION(gf,multiply,w128,NULL) + SET_FUNCTION(gf,divide,w128,NULL) + SET_FUNCTION(gf,inverse,w128,NULL) + SET_FUNCTION(gf,multiply_region,w128,NULL) + switch(h->mult_type) { + case GF_MULT_BYTWO_p: + case GF_MULT_BYTWO_b: if (gf_w128_bytwo_init(gf) == 0) return 0; break; + case GF_MULT_CARRY_FREE: if (gf_w128_cfm_init(gf) == 0) return 0; break; + case GF_MULT_SHIFT: if (gf_w128_shift_init(gf) == 0) return 0; break; + case GF_MULT_GROUP: if (gf_w128_group_init(gf) == 0) return 0; break; + case GF_MULT_DEFAULT: + case GF_MULT_SPLIT_TABLE: if (gf_w128_split_init(gf) == 0) return 0; break; + case GF_MULT_COMPOSITE: if (gf_w128_composite_init(gf) == 0) return 0; break; + default: return 0; + } + + /* Ben: Used to be h->region_type == GF_REGION_ALTMAP, but failed since there + are multiple flags in h->region_type */ + if (h->mult_type == GF_MULT_SPLIT_TABLE && (h->region_type & GF_REGION_ALTMAP)) { + SET_FUNCTION(gf,extract_word,w128,gf_w128_split_extract_word) + } else if (h->mult_type == GF_MULT_COMPOSITE && h->region_type == GF_REGION_ALTMAP) { + SET_FUNCTION(gf,extract_word,w128,gf_w128_composite_extract_word) + } else { + SET_FUNCTION(gf,extract_word,w128,gf_w128_extract_word) + } + + if (h->divide_type == GF_DIVIDE_EUCLID) { + SET_FUNCTION(gf,divide,w128,gf_w128_divide_from_inverse) + } + + if (gf->inverse.w128 != NULL && gf->divide.w128 == NULL) { + SET_FUNCTION(gf,divide,w128,gf_w128_divide_from_inverse) + } + if (gf->inverse.w128 == NULL && gf->divide.w128 != NULL) { + SET_FUNCTION(gf,inverse,w128,gf_w128_inverse_from_divide) + } + return 1; +} diff --git a/IDA_new/gf-complete/src/gf_w16.c b/IDA_new/gf-complete/src/gf_w16.c new file mode 100644 index 0000000..8316892 --- /dev/null +++ b/IDA_new/gf-complete/src/gf_w16.c @@ -0,0 +1,2449 @@ +/* + * GF-Complete: A Comprehensive Open Source Library for Galois Field Arithmetic + * James S. Plank, Ethan L. Miller, Kevin M. Greenan, + * Benjamin A. Arnold, John A. Burnum, Adam W. Disney, Allen C. McBride. + * + * gf_w16.c + * + * Routines for 16-bit Galois fields + */ + +#include "gf_int.h" +#include +#include +#include "gf_w16.h" +#include "gf_cpu.h" + +#define AB2(ip, am1 ,am2, b, t1, t2) {\ + t1 = (b << 1) & am1;\ + t2 = b & am2; \ + t2 = ((t2 << 1) - (t2 >> (GF_FIELD_WIDTH-1))); \ + b = (t1 ^ (t2 & ip));} + +#define SSE_AB2(pp, m1 ,m2, va, t1, t2) {\ + t1 = _mm_and_si128(_mm_slli_epi64(va, 1), m1); \ + t2 = _mm_and_si128(va, m2); \ + t2 = _mm_sub_epi64 (_mm_slli_epi64(t2, 1), _mm_srli_epi64(t2, (GF_FIELD_WIDTH-1))); \ + va = _mm_xor_si128(t1, _mm_and_si128(t2, pp)); } + +#define MM_PRINT(s, r) { uint8_t blah[16], ii; printf("%-12s", s); _mm_storeu_si128((__m128i *)blah, r); for (ii = 0; ii < 16; ii += 2) printf(" %02x %02x", blah[15-ii], blah[14-ii]); printf("\n"); } + +#define GF_FIRST_BIT (1 << 15) +#define GF_MULTBY_TWO(p) (((p) & GF_FIRST_BIT) ? (((p) << 1) ^ h->prim_poly) : (p) << 1) + +static +inline +gf_val_32_t gf_w16_inverse_from_divide (gf_t *gf, gf_val_32_t a) +{ + return gf->divide.w32(gf, 1, a); +} + +static +inline +gf_val_32_t gf_w16_divide_from_inverse (gf_t *gf, gf_val_32_t a, gf_val_32_t b) +{ + b = gf->inverse.w32(gf, b); + return gf->multiply.w32(gf, a, b); +} + +static +void +gf_w16_multiply_region_from_single(gf_t *gf, void *src, void *dest, gf_val_32_t val, int bytes, int xor) +{ + gf_region_data rd; + uint16_t *s16; + uint16_t *d16; + + if (val == 0) { gf_multby_zero(dest, bytes, xor); return; } + if (val == 1) { gf_multby_one(src, dest, bytes, xor); return; } + + gf_set_region_data(&rd, gf, src, dest, bytes, val, xor, 2); + gf_do_initial_region_alignment(&rd); + + s16 = (uint16_t *) rd.s_start; + d16 = (uint16_t *) rd.d_start; + + if (xor) { + while (d16 < ((uint16_t *) rd.d_top)) { + *d16 ^= gf->multiply.w32(gf, val, *s16); + d16++; + s16++; + } + } else { + while (d16 < ((uint16_t *) rd.d_top)) { + *d16 = gf->multiply.w32(gf, val, *s16); + d16++; + s16++; + } + } + gf_do_final_region_alignment(&rd); +} + +#if defined(INTEL_SSE4_PCLMUL) +static +void +gf_w16_clm_multiply_region_from_single_2(gf_t *gf, void *src, void *dest, gf_val_32_t val, int bytes, int xor) +{ + gf_region_data rd; + uint16_t *s16; + uint16_t *d16; + __m128i a, b; + __m128i result; + __m128i prim_poly; + __m128i w; + gf_internal_t * h = gf->scratch; + prim_poly = _mm_set_epi32(0, 0, 0, (uint32_t)(h->prim_poly & 0x1ffffULL)); + + if (val == 0) { gf_multby_zero(dest, bytes, xor); return; } + if (val == 1) { gf_multby_one(src, dest, bytes, xor); return; } + + gf_set_region_data(&rd, gf, src, dest, bytes, val, xor, 2); + gf_do_initial_region_alignment(&rd); + + a = _mm_insert_epi32 (_mm_setzero_si128(), val, 0); + + s16 = (uint16_t *) rd.s_start; + d16 = (uint16_t *) rd.d_start; + + if (xor) { + while (d16 < ((uint16_t *) rd.d_top)) { + + /* see gf_w16_clm_multiply() to see explanation of method */ + + b = _mm_insert_epi32 (a, (gf_val_32_t)(*s16), 0); + result = _mm_clmulepi64_si128 (a, b, 0); + w = _mm_clmulepi64_si128 (prim_poly, _mm_srli_si128 (result, 2), 0); + result = _mm_xor_si128 (result, w); + w = _mm_clmulepi64_si128 (prim_poly, _mm_srli_si128 (result, 2), 0); + result = _mm_xor_si128 (result, w); + + *d16 ^= ((gf_val_32_t)_mm_extract_epi32(result, 0)); + d16++; + s16++; + } + } else { + while (d16 < ((uint16_t *) rd.d_top)) { + + /* see gf_w16_clm_multiply() to see explanation of method */ + + b = _mm_insert_epi32 (a, (gf_val_32_t)(*s16), 0); + result = _mm_clmulepi64_si128 (a, b, 0); + w = _mm_clmulepi64_si128 (prim_poly, _mm_srli_si128 (result, 2), 0); + result = _mm_xor_si128 (result, w); + w = _mm_clmulepi64_si128 (prim_poly, _mm_srli_si128 (result, 2), 0); + result = _mm_xor_si128 (result, w); + + *d16 = ((gf_val_32_t)_mm_extract_epi32(result, 0)); + d16++; + s16++; + } + } + gf_do_final_region_alignment(&rd); +} +#endif + +#if defined(INTEL_SSE4_PCLMUL) +static +void +gf_w16_clm_multiply_region_from_single_3(gf_t *gf, void *src, void *dest, gf_val_32_t val, int bytes, int xor) +{ + gf_region_data rd; + uint16_t *s16; + uint16_t *d16; + + __m128i a, b; + __m128i result; + __m128i prim_poly; + __m128i w; + gf_internal_t * h = gf->scratch; + prim_poly = _mm_set_epi32(0, 0, 0, (uint32_t)(h->prim_poly & 0x1ffffULL)); + + if (val == 0) { gf_multby_zero(dest, bytes, xor); return; } + if (val == 1) { gf_multby_one(src, dest, bytes, xor); return; } + + a = _mm_insert_epi32 (_mm_setzero_si128(), val, 0); + + gf_set_region_data(&rd, gf, src, dest, bytes, val, xor, 2); + gf_do_initial_region_alignment(&rd); + + s16 = (uint16_t *) rd.s_start; + d16 = (uint16_t *) rd.d_start; + + if (xor) { + while (d16 < ((uint16_t *) rd.d_top)) { + + /* see gf_w16_clm_multiply() to see explanation of method */ + + b = _mm_insert_epi32 (a, (gf_val_32_t)(*s16), 0); + result = _mm_clmulepi64_si128 (a, b, 0); + w = _mm_clmulepi64_si128 (prim_poly, _mm_srli_si128 (result, 2), 0); + result = _mm_xor_si128 (result, w); + w = _mm_clmulepi64_si128 (prim_poly, _mm_srli_si128 (result, 2), 0); + result = _mm_xor_si128 (result, w); + w = _mm_clmulepi64_si128 (prim_poly, _mm_srli_si128 (result, 2), 0); + result = _mm_xor_si128 (result, w); + + *d16 ^= ((gf_val_32_t)_mm_extract_epi32(result, 0)); + d16++; + s16++; + } + } else { + while (d16 < ((uint16_t *) rd.d_top)) { + + /* see gf_w16_clm_multiply() to see explanation of method */ + + b = _mm_insert_epi32 (a, (gf_val_32_t)(*s16), 0); + result = _mm_clmulepi64_si128 (a, b, 0); + w = _mm_clmulepi64_si128 (prim_poly, _mm_srli_si128 (result, 2), 0); + result = _mm_xor_si128 (result, w); + w = _mm_clmulepi64_si128 (prim_poly, _mm_srli_si128 (result, 2), 0); + result = _mm_xor_si128 (result, w); + w = _mm_clmulepi64_si128 (prim_poly, _mm_srli_si128 (result, 2), 0); + result = _mm_xor_si128 (result, w); + + *d16 = ((gf_val_32_t)_mm_extract_epi32(result, 0)); + d16++; + s16++; + } + } + gf_do_final_region_alignment(&rd); +} +#endif + +#if defined(INTEL_SSE4_PCLMUL) +static +void +gf_w16_clm_multiply_region_from_single_4(gf_t *gf, void *src, void *dest, gf_val_32_t val, int bytes, int xor) +{ + gf_region_data rd; + uint16_t *s16; + uint16_t *d16; + + __m128i a, b; + __m128i result; + __m128i prim_poly; + __m128i w; + gf_internal_t * h = gf->scratch; + prim_poly = _mm_set_epi32(0, 0, 0, (uint32_t)(h->prim_poly & 0x1ffffULL)); + + if (val == 0) { gf_multby_zero(dest, bytes, xor); return; } + if (val == 1) { gf_multby_one(src, dest, bytes, xor); return; } + + gf_set_region_data(&rd, gf, src, dest, bytes, val, xor, 2); + gf_do_initial_region_alignment(&rd); + + a = _mm_insert_epi32 (_mm_setzero_si128(), val, 0); + + s16 = (uint16_t *) rd.s_start; + d16 = (uint16_t *) rd.d_start; + + if (xor) { + while (d16 < ((uint16_t *) rd.d_top)) { + + /* see gf_w16_clm_multiply() to see explanation of method */ + + b = _mm_insert_epi32 (a, (gf_val_32_t)(*s16), 0); + result = _mm_clmulepi64_si128 (a, b, 0); + w = _mm_clmulepi64_si128 (prim_poly, _mm_srli_si128 (result, 2), 0); + result = _mm_xor_si128 (result, w); + w = _mm_clmulepi64_si128 (prim_poly, _mm_srli_si128 (result, 2), 0); + result = _mm_xor_si128 (result, w); + w = _mm_clmulepi64_si128 (prim_poly, _mm_srli_si128 (result, 2), 0); + result = _mm_xor_si128 (result, w); + w = _mm_clmulepi64_si128 (prim_poly, _mm_srli_si128 (result, 2), 0); + result = _mm_xor_si128 (result, w); + + *d16 ^= ((gf_val_32_t)_mm_extract_epi32(result, 0)); + d16++; + s16++; + } + } else { + while (d16 < ((uint16_t *) rd.d_top)) { + + /* see gf_w16_clm_multiply() to see explanation of method */ + + b = _mm_insert_epi32 (a, (gf_val_32_t)(*s16), 0); + result = _mm_clmulepi64_si128 (a, b, 0); + w = _mm_clmulepi64_si128 (prim_poly, _mm_srli_si128 (result, 2), 0); + result = _mm_xor_si128 (result, w); + w = _mm_clmulepi64_si128 (prim_poly, _mm_srli_si128 (result, 2), 0); + result = _mm_xor_si128 (result, w); + w = _mm_clmulepi64_si128 (prim_poly, _mm_srli_si128 (result, 2), 0); + result = _mm_xor_si128 (result, w); + w = _mm_clmulepi64_si128 (prim_poly, _mm_srli_si128 (result, 2), 0); + result = _mm_xor_si128 (result, w); + + *d16 = ((gf_val_32_t)_mm_extract_epi32(result, 0)); + d16++; + s16++; + } + } + gf_do_final_region_alignment(&rd); +} +#endif + +static +inline +gf_val_32_t gf_w16_euclid (gf_t *gf, gf_val_32_t b) +{ + gf_val_32_t e_i, e_im1, e_ip1; + gf_val_32_t d_i, d_im1, d_ip1; + gf_val_32_t y_i, y_im1, y_ip1; + gf_val_32_t c_i; + + if (b == 0) return -1; + e_im1 = ((gf_internal_t *) (gf->scratch))->prim_poly; + e_i = b; + d_im1 = 16; + for (d_i = d_im1; ((1 << d_i) & e_i) == 0; d_i--) ; + y_i = 1; + y_im1 = 0; + + while (e_i != 1) { + + e_ip1 = e_im1; + d_ip1 = d_im1; + c_i = 0; + + while (d_ip1 >= d_i) { + c_i ^= (1 << (d_ip1 - d_i)); + e_ip1 ^= (e_i << (d_ip1 - d_i)); + if (e_ip1 == 0) return 0; + while ((e_ip1 & (1 << d_ip1)) == 0) d_ip1--; + } + + y_ip1 = y_im1 ^ gf->multiply.w32(gf, c_i, y_i); + y_im1 = y_i; + y_i = y_ip1; + + e_im1 = e_i; + d_im1 = d_i; + e_i = e_ip1; + d_i = d_ip1; + } + + return y_i; +} + +static +gf_val_32_t gf_w16_extract_word(gf_t *gf, void *start, int bytes, int index) +{ + uint16_t *r16, rv; + + r16 = (uint16_t *) start; + rv = r16[index]; + return rv; +} + +static +gf_val_32_t gf_w16_composite_extract_word(gf_t *gf, void *start, int bytes, int index) +{ + int sub_size; + gf_internal_t *h; + uint8_t *r8, *top; + uint16_t a, b, *r16; + gf_region_data rd; + + h = (gf_internal_t *) gf->scratch; + gf_set_region_data(&rd, gf, start, start, bytes, 0, 0, 32); + r16 = (uint16_t *) start; + if (r16 + index < (uint16_t *) rd.d_start) return r16[index]; + if (r16 + index >= (uint16_t *) rd.d_top) return r16[index]; + index -= (((uint16_t *) rd.d_start) - r16); + r8 = (uint8_t *) rd.d_start; + top = (uint8_t *) rd.d_top; + sub_size = (top-r8)/2; + + a = h->base_gf->extract_word.w32(h->base_gf, r8, sub_size, index); + b = h->base_gf->extract_word.w32(h->base_gf, r8+sub_size, sub_size, index); + return (a | (b << 8)); +} + +static +gf_val_32_t gf_w16_split_extract_word(gf_t *gf, void *start, int bytes, int index) +{ + uint16_t *r16, rv; + uint8_t *r8; + gf_region_data rd; + + gf_set_region_data(&rd, gf, start, start, bytes, 0, 0, 32); + r16 = (uint16_t *) start; + if (r16 + index < (uint16_t *) rd.d_start) return r16[index]; + if (r16 + index >= (uint16_t *) rd.d_top) return r16[index]; + index -= (((uint16_t *) rd.d_start) - r16); + r8 = (uint8_t *) rd.d_start; + r8 += ((index & 0xfffffff0)*2); + r8 += (index & 0xf); + rv = (*r8 << 8); + r8 += 16; + rv |= *r8; + return rv; +} + +static +inline +gf_val_32_t gf_w16_matrix (gf_t *gf, gf_val_32_t b) +{ + return gf_bitmatrix_inverse(b, 16, ((gf_internal_t *) (gf->scratch))->prim_poly); +} + +/* JSP: GF_MULT_SHIFT: The world's dumbest multiplication algorithm. I only + include it for completeness. It does have the feature that it requires no + extra memory. + */ + +#if defined(INTEL_SSE4_PCLMUL) +static +inline +gf_val_32_t +gf_w16_clm_multiply_2 (gf_t *gf, gf_val_32_t a16, gf_val_32_t b16) +{ + gf_val_32_t rv = 0; + + __m128i a, b; + __m128i result; + __m128i prim_poly; + __m128i w; + gf_internal_t * h = gf->scratch; + + a = _mm_insert_epi32 (_mm_setzero_si128(), a16, 0); + b = _mm_insert_epi32 (a, b16, 0); + + prim_poly = _mm_set_epi32(0, 0, 0, (uint32_t)(h->prim_poly & 0x1ffffULL)); + + /* Do the initial multiply */ + + result = _mm_clmulepi64_si128 (a, b, 0); + + /* Ben: Do prim_poly reduction twice. We are guaranteed that we will only + have to do the reduction at most twice, because (w-2)/z == 2. Where + z is equal to the number of zeros after the leading 1 + + _mm_clmulepi64_si128 is the carryless multiply operation. Here + _mm_srli_si128 shifts the result to the right by 2 bytes. This allows + us to multiply the prim_poly by the leading bits of the result. We + then xor the result of that operation back with the result.*/ + + w = _mm_clmulepi64_si128 (prim_poly, _mm_srli_si128 (result, 2), 0); + result = _mm_xor_si128 (result, w); + w = _mm_clmulepi64_si128 (prim_poly, _mm_srli_si128 (result, 2), 0); + result = _mm_xor_si128 (result, w); + + /* Extracts 32 bit value from result. */ + + rv = ((gf_val_32_t)_mm_extract_epi32(result, 0)); + + return rv; +} +#endif + +#if defined(INTEL_SSE4_PCLMUL) +static +inline +gf_val_32_t +gf_w16_clm_multiply_3 (gf_t *gf, gf_val_32_t a16, gf_val_32_t b16) +{ + gf_val_32_t rv = 0; + + __m128i a, b; + __m128i result; + __m128i prim_poly; + __m128i w; + gf_internal_t * h = gf->scratch; + + a = _mm_insert_epi32 (_mm_setzero_si128(), a16, 0); + b = _mm_insert_epi32 (a, b16, 0); + + prim_poly = _mm_set_epi32(0, 0, 0, (uint32_t)(h->prim_poly & 0x1ffffULL)); + + /* Do the initial multiply */ + + result = _mm_clmulepi64_si128 (a, b, 0); + + w = _mm_clmulepi64_si128 (prim_poly, _mm_srli_si128 (result, 2), 0); + result = _mm_xor_si128 (result, w); + w = _mm_clmulepi64_si128 (prim_poly, _mm_srli_si128 (result, 2), 0); + result = _mm_xor_si128 (result, w); + w = _mm_clmulepi64_si128 (prim_poly, _mm_srli_si128 (result, 2), 0); + result = _mm_xor_si128 (result, w); + + /* Extracts 32 bit value from result. */ + + rv = ((gf_val_32_t)_mm_extract_epi32(result, 0)); + + return rv; +} +#endif + +#if defined(INTEL_SSE4_PCLMUL) +static +inline +gf_val_32_t +gf_w16_clm_multiply_4 (gf_t *gf, gf_val_32_t a16, gf_val_32_t b16) +{ + gf_val_32_t rv = 0; + + __m128i a, b; + __m128i result; + __m128i prim_poly; + __m128i w; + gf_internal_t * h = gf->scratch; + + a = _mm_insert_epi32 (_mm_setzero_si128(), a16, 0); + b = _mm_insert_epi32 (a, b16, 0); + + prim_poly = _mm_set_epi32(0, 0, 0, (uint32_t)(h->prim_poly & 0x1ffffULL)); + + /* Do the initial multiply */ + + result = _mm_clmulepi64_si128 (a, b, 0); + + w = _mm_clmulepi64_si128 (prim_poly, _mm_srli_si128 (result, 2), 0); + result = _mm_xor_si128 (result, w); + w = _mm_clmulepi64_si128 (prim_poly, _mm_srli_si128 (result, 2), 0); + result = _mm_xor_si128 (result, w); + w = _mm_clmulepi64_si128 (prim_poly, _mm_srli_si128 (result, 2), 0); + result = _mm_xor_si128 (result, w); + w = _mm_clmulepi64_si128 (prim_poly, _mm_srli_si128 (result, 2), 0); + result = _mm_xor_si128 (result, w); + + /* Extracts 32 bit value from result. */ + + rv = ((gf_val_32_t)_mm_extract_epi32(result, 0)); + + return rv; +} +#endif + + +static +inline + gf_val_32_t +gf_w16_shift_multiply (gf_t *gf, gf_val_32_t a16, gf_val_32_t b16) +{ + gf_val_32_t product, i, pp, a, b; + gf_internal_t *h; + + a = a16; + b = b16; + h = (gf_internal_t *) gf->scratch; + pp = h->prim_poly; + + product = 0; + + for (i = 0; i < GF_FIELD_WIDTH; i++) { + if (a & (1 << i)) product ^= (b << i); + } + for (i = (GF_FIELD_WIDTH*2-2); i >= GF_FIELD_WIDTH; i--) { + if (product & (1 << i)) product ^= (pp << (i-GF_FIELD_WIDTH)); + } + return product; +} + +static +int gf_w16_shift_init(gf_t *gf) +{ + SET_FUNCTION(gf,multiply,w32,gf_w16_shift_multiply) + return 1; +} + +static +int gf_w16_cfm_init(gf_t *gf) +{ +#if defined(INTEL_SSE4_PCLMUL) + if (gf_cpu_supports_intel_pclmul) { + gf_internal_t *h; + + h = (gf_internal_t *) gf->scratch; + + /*Ben: Determining how many reductions to do */ + + if ((0xfe00 & h->prim_poly) == 0) { + SET_FUNCTION(gf,multiply,w32,gf_w16_clm_multiply_2) + SET_FUNCTION(gf,multiply_region,w32,gf_w16_clm_multiply_region_from_single_2) + } else if((0xf000 & h->prim_poly) == 0) { + SET_FUNCTION(gf,multiply,w32,gf_w16_clm_multiply_3) + SET_FUNCTION(gf,multiply_region,w32,gf_w16_clm_multiply_region_from_single_3) + } else if ((0xe000 & h->prim_poly) == 0) { + SET_FUNCTION(gf,multiply,w32,gf_w16_clm_multiply_4) + SET_FUNCTION(gf,multiply_region,w32,gf_w16_clm_multiply_region_from_single_4) + } else { + return 0; + } + return 1; + } +#endif + + return 0; +} + +/* KMG: GF_MULT_LOGTABLE: */ + +static +void +gf_w16_log_multiply_region(gf_t *gf, void *src, void *dest, gf_val_32_t val, int bytes, int xor) +{ + uint16_t *s16, *d16; + int lv; + struct gf_w16_logtable_data *ltd; + gf_region_data rd; + + if (val == 0) { gf_multby_zero(dest, bytes, xor); return; } + if (val == 1) { gf_multby_one(src, dest, bytes, xor); return; } + + gf_set_region_data(&rd, gf, src, dest, bytes, val, xor, 2); + gf_do_initial_region_alignment(&rd); + + ltd = (struct gf_w16_logtable_data *) ((gf_internal_t *) gf->scratch)->private; + s16 = (uint16_t *) rd.s_start; + d16 = (uint16_t *) rd.d_start; + + lv = ltd->log_tbl[val]; + + if (xor) { + while (d16 < (uint16_t *) rd.d_top) { + *d16 ^= (*s16 == 0 ? 0 : ltd->antilog_tbl[lv + ltd->log_tbl[*s16]]); + d16++; + s16++; + } + } else { + while (d16 < (uint16_t *) rd.d_top) { + *d16 = (*s16 == 0 ? 0 : ltd->antilog_tbl[lv + ltd->log_tbl[*s16]]); + d16++; + s16++; + } + } + gf_do_final_region_alignment(&rd); +} + +static +inline +gf_val_32_t +gf_w16_log_multiply(gf_t *gf, gf_val_32_t a, gf_val_32_t b) +{ + struct gf_w16_logtable_data *ltd; + + ltd = (struct gf_w16_logtable_data *) ((gf_internal_t *) gf->scratch)->private; + return (a == 0 || b == 0) ? 0 : ltd->antilog_tbl[(int) ltd->log_tbl[a] + (int) ltd->log_tbl[b]]; +} + +static +inline +gf_val_32_t +gf_w16_log_divide(gf_t *gf, gf_val_32_t a, gf_val_32_t b) +{ + int log_sum = 0; + struct gf_w16_logtable_data *ltd; + + if (a == 0 || b == 0) return 0; + ltd = (struct gf_w16_logtable_data *) ((gf_internal_t *) gf->scratch)->private; + + log_sum = (int) ltd->log_tbl[a] - (int) ltd->log_tbl[b]; + return (ltd->d_antilog[log_sum]); +} + +static +gf_val_32_t +gf_w16_log_inverse(gf_t *gf, gf_val_32_t a) +{ + struct gf_w16_logtable_data *ltd; + + ltd = (struct gf_w16_logtable_data *) ((gf_internal_t *) gf->scratch)->private; + return (ltd->inv_tbl[a]); +} + +static +int gf_w16_log_init(gf_t *gf) +{ + gf_internal_t *h; + struct gf_w16_logtable_data *ltd; + int i, b; + int check = 0; + + h = (gf_internal_t *) gf->scratch; + ltd = h->private; + + for (i = 0; i < GF_MULT_GROUP_SIZE+1; i++) + ltd->log_tbl[i] = 0; + ltd->d_antilog = ltd->antilog_tbl + GF_MULT_GROUP_SIZE; + + b = 1; + for (i = 0; i < GF_MULT_GROUP_SIZE; i++) { + if (ltd->log_tbl[b] != 0) check = 1; + ltd->log_tbl[b] = i; + ltd->antilog_tbl[i] = b; + ltd->antilog_tbl[i+GF_MULT_GROUP_SIZE] = b; + b <<= 1; + if (b & GF_FIELD_SIZE) { + b = b ^ h->prim_poly; + } + } + + /* If you can't construct the log table, there's a problem. This code is used for + some other implementations (e.g. in SPLIT), so if the log table doesn't work in + that instance, use CARRY_FREE / SHIFT instead. */ + + if (check) { + if (h->mult_type != GF_MULT_LOG_TABLE) { + if (gf_cpu_supports_intel_pclmul) { + return gf_w16_cfm_init(gf); + } + return gf_w16_shift_init(gf); + } else { + _gf_errno = GF_E_LOGPOLY; + return 0; + } + } + + ltd->inv_tbl[0] = 0; /* Not really, but we need to fill it with something */ + ltd->inv_tbl[1] = 1; + for (i = 2; i < GF_FIELD_SIZE; i++) { + ltd->inv_tbl[i] = ltd->antilog_tbl[GF_MULT_GROUP_SIZE-ltd->log_tbl[i]]; + } + + SET_FUNCTION(gf,inverse,w32,gf_w16_log_inverse) + SET_FUNCTION(gf,divide,w32,gf_w16_log_divide) + SET_FUNCTION(gf,multiply,w32,gf_w16_log_multiply) + SET_FUNCTION(gf,multiply_region,w32,gf_w16_log_multiply_region) + + return 1; +} + +/* JSP: GF_MULT_SPLIT_TABLE: Using 8 multiplication tables to leverage SSE instructions. +*/ + + +/* Ben: Does alternate mapping multiplication using a split table in the + lazy method without sse instructions*/ + +static +void +gf_w16_split_4_16_lazy_nosse_altmap_multiply_region(gf_t *gf, void *src, void *dest, gf_val_32_t val, int bytes, int xor) +{ + uint64_t i, j, c, prod; + uint8_t *s8, *d8, *top; + uint16_t table[4][16]; + gf_region_data rd; + + if (val == 0) { gf_multby_zero(dest, bytes, xor); return; } + if (val == 1) { gf_multby_one(src, dest, bytes, xor); return; } + + gf_set_region_data(&rd, gf, src, dest, bytes, val, xor, 32); + gf_do_initial_region_alignment(&rd); + + /*Ben: Constructs lazy multiplication table*/ + + for (j = 0; j < 16; j++) { + for (i = 0; i < 4; i++) { + c = (j << (i*4)); + table[i][j] = gf->multiply.w32(gf, c, val); + } + } + + /*Ben: s8 is the start of source, d8 is the start of dest, top is end of dest region. */ + + s8 = (uint8_t *) rd.s_start; + d8 = (uint8_t *) rd.d_start; + top = (uint8_t *) rd.d_top; + + + while (d8 < top) { + + /*Ben: Multiplies across 16 two byte quantities using alternate mapping + high bits are on the left, low bits are on the right. */ + + for (j=0;j<16;j++) { + + /*Ben: If the xor flag is set, the product should include what is in dest */ + prod = (xor) ? ((uint16_t)(*d8)<<8) ^ *(d8+16) : 0; + + /*Ben: xors all 4 table lookups into the product variable*/ + + prod ^= ((table[0][*(s8+16)&0xf]) ^ + (table[1][(*(s8+16)&0xf0)>>4]) ^ + (table[2][*(s8)&0xf]) ^ + (table[3][(*(s8)&0xf0)>>4])); + + /*Ben: Stores product in the destination and moves on*/ + + *d8 = (uint8_t)(prod >> 8); + *(d8+16) = (uint8_t)(prod & 0x00ff); + s8++; + d8++; + } + s8+=16; + d8+=16; + } + gf_do_final_region_alignment(&rd); +} + +static + void +gf_w16_split_4_16_lazy_multiply_region(gf_t *gf, void *src, void *dest, gf_val_32_t val, int bytes, int xor) +{ + uint64_t i, j, a, c, prod; + uint16_t *s16, *d16, *top; + uint16_t table[4][16]; + gf_region_data rd; + + if (val == 0) { gf_multby_zero(dest, bytes, xor); return; } + if (val == 1) { gf_multby_one(src, dest, bytes, xor); return; } + + gf_set_region_data(&rd, gf, src, dest, bytes, val, xor, 2); + gf_do_initial_region_alignment(&rd); + + for (j = 0; j < 16; j++) { + for (i = 0; i < 4; i++) { + c = (j << (i*4)); + table[i][j] = gf->multiply.w32(gf, c, val); + } + } + + s16 = (uint16_t *) rd.s_start; + d16 = (uint16_t *) rd.d_start; + top = (uint16_t *) rd.d_top; + + while (d16 < top) { + a = *s16; + prod = (xor) ? *d16 : 0; + for (i = 0; i < 4; i++) { + prod ^= table[i][a&0xf]; + a >>= 4; + } + *d16 = prod; + s16++; + d16++; + } +} + +static +void +gf_w16_split_8_16_lazy_multiply_region(gf_t *gf, void *src, void *dest, gf_val_32_t val, int bytes, int xor) +{ + uint64_t j, k, v, a, prod, *s64, *d64, *top64; + gf_internal_t *h; + uint64_t htable[256], ltable[256]; + gf_region_data rd; + + if (val == 0) { gf_multby_zero(dest, bytes, xor); return; } + if (val == 1) { gf_multby_one(src, dest, bytes, xor); return; } + + gf_set_region_data(&rd, gf, src, dest, bytes, val, xor, 8); + gf_do_initial_region_alignment(&rd); + + h = (gf_internal_t *) gf->scratch; + + v = val; + ltable[0] = 0; + for (j = 1; j < 256; j <<= 1) { + for (k = 0; k < j; k++) ltable[k^j] = (v ^ ltable[k]); + v = GF_MULTBY_TWO(v); + } + htable[0] = 0; + for (j = 1; j < 256; j <<= 1) { + for (k = 0; k < j; k++) htable[k^j] = (v ^ htable[k]); + v = GF_MULTBY_TWO(v); + } + + s64 = (uint64_t *) rd.s_start; + d64 = (uint64_t *) rd.d_start; + top64 = (uint64_t *) rd.d_top; + +/* Does Unrolling Matter? -- Doesn't seem to. + while (d64 != top64) { + a = *s64; + + prod = htable[a >> 56]; + a <<= 8; + prod ^= ltable[a >> 56]; + a <<= 8; + prod <<= 16; + + prod ^= htable[a >> 56]; + a <<= 8; + prod ^= ltable[a >> 56]; + a <<= 8; + prod <<= 16; + + prod ^= htable[a >> 56]; + a <<= 8; + prod ^= ltable[a >> 56]; + a <<= 8; + prod <<= 16; + + prod ^= htable[a >> 56]; + a <<= 8; + prod ^= ltable[a >> 56]; + prod ^= ((xor) ? *d64 : 0); + *d64 = prod; + s64++; + d64++; + } +*/ + + while (d64 != top64) { + a = *s64; + + prod = 0; + for (j = 0; j < 4; j++) { + prod <<= 16; + prod ^= htable[a >> 56]; + a <<= 8; + prod ^= ltable[a >> 56]; + a <<= 8; + } + + //JSP: We can move the conditional outside the while loop, but we need to fully test it to understand which is better. + + prod ^= ((xor) ? *d64 : 0); + *d64 = prod; + s64++; + d64++; + } + gf_do_final_region_alignment(&rd); +} + +static void +gf_w16_table_lazy_multiply_region(gf_t *gf, void *src, void *dest, gf_val_32_t val, int bytes, int xor) +{ + uint64_t c; + gf_internal_t *h; + struct gf_w16_lazytable_data *ltd; + gf_region_data rd; + + if (val == 0) { gf_multby_zero(dest, bytes, xor); return; } + if (val == 1) { gf_multby_one(src, dest, bytes, xor); return; } + + gf_set_region_data(&rd, gf, src, dest, bytes, val, xor, 8); + gf_do_initial_region_alignment(&rd); + + h = (gf_internal_t *) gf->scratch; + ltd = (struct gf_w16_lazytable_data *) h->private; + + ltd->lazytable[0] = 0; + + /* + a = val; + c = 1; + pp = h->prim_poly; + + do { + ltd->lazytable[c] = a; + c <<= 1; + if (c & (1 << GF_FIELD_WIDTH)) c ^= pp; + a <<= 1; + if (a & (1 << GF_FIELD_WIDTH)) a ^= pp; + } while (c != 1); + */ + + for (c = 1; c < GF_FIELD_SIZE; c++) { + ltd->lazytable[c] = gf_w16_shift_multiply(gf, c, val); + } + + gf_two_byte_region_table_multiply(&rd, ltd->lazytable); + gf_do_final_region_alignment(&rd); +} + +#ifdef INTEL_SSSE3 +static +void +gf_w16_split_4_16_lazy_sse_multiply_region(gf_t *gf, void *src, void *dest, gf_val_32_t val, int bytes, int xor) +{ + uint64_t i, j, *s64, *d64, *top64;; + uint64_t c, prod; + uint8_t low[4][16]; + uint8_t high[4][16]; + gf_region_data rd; + + __m128i mask, ta, tb, ti, tpl, tph, tlow[4], thigh[4], tta, ttb, lmask; + + if (val == 0) { gf_multby_zero(dest, bytes, xor); return; } + if (val == 1) { gf_multby_one(src, dest, bytes, xor); return; } + + gf_set_region_data(&rd, gf, src, dest, bytes, val, xor, 32); + gf_do_initial_region_alignment(&rd); + + for (j = 0; j < 16; j++) { + for (i = 0; i < 4; i++) { + c = (j << (i*4)); + prod = gf->multiply.w32(gf, c, val); + low[i][j] = (prod & 0xff); + high[i][j] = (prod >> 8); + } + } + + for (i = 0; i < 4; i++) { + tlow[i] = _mm_loadu_si128((__m128i *)low[i]); + thigh[i] = _mm_loadu_si128((__m128i *)high[i]); + } + + s64 = (uint64_t *) rd.s_start; + d64 = (uint64_t *) rd.d_start; + top64 = (uint64_t *) rd.d_top; + + mask = _mm_set1_epi8 (0x0f); + lmask = _mm_set1_epi16 (0xff); + + if (xor) { + while (d64 != top64) { + + ta = _mm_load_si128((__m128i *) s64); + tb = _mm_load_si128((__m128i *) (s64+2)); + + tta = _mm_srli_epi16(ta, 8); + ttb = _mm_srli_epi16(tb, 8); + tpl = _mm_and_si128(tb, lmask); + tph = _mm_and_si128(ta, lmask); + + tb = _mm_packus_epi16(tpl, tph); + ta = _mm_packus_epi16(ttb, tta); + + ti = _mm_and_si128 (mask, tb); + tph = _mm_shuffle_epi8 (thigh[0], ti); + tpl = _mm_shuffle_epi8 (tlow[0], ti); + + tb = _mm_srli_epi16(tb, 4); + ti = _mm_and_si128 (mask, tb); + tpl = _mm_xor_si128(_mm_shuffle_epi8 (tlow[1], ti), tpl); + tph = _mm_xor_si128(_mm_shuffle_epi8 (thigh[1], ti), tph); + + ti = _mm_and_si128 (mask, ta); + tpl = _mm_xor_si128(_mm_shuffle_epi8 (tlow[2], ti), tpl); + tph = _mm_xor_si128(_mm_shuffle_epi8 (thigh[2], ti), tph); + + ta = _mm_srli_epi16(ta, 4); + ti = _mm_and_si128 (mask, ta); + tpl = _mm_xor_si128(_mm_shuffle_epi8 (tlow[3], ti), tpl); + tph = _mm_xor_si128(_mm_shuffle_epi8 (thigh[3], ti), tph); + + ta = _mm_unpackhi_epi8(tpl, tph); + tb = _mm_unpacklo_epi8(tpl, tph); + + tta = _mm_load_si128((__m128i *) d64); + ta = _mm_xor_si128(ta, tta); + ttb = _mm_load_si128((__m128i *) (d64+2)); + tb = _mm_xor_si128(tb, ttb); + _mm_store_si128 ((__m128i *)d64, ta); + _mm_store_si128 ((__m128i *)(d64+2), tb); + + d64 += 4; + s64 += 4; + + } + } else { + while (d64 != top64) { + + ta = _mm_load_si128((__m128i *) s64); + tb = _mm_load_si128((__m128i *) (s64+2)); + + tta = _mm_srli_epi16(ta, 8); + ttb = _mm_srli_epi16(tb, 8); + tpl = _mm_and_si128(tb, lmask); + tph = _mm_and_si128(ta, lmask); + + tb = _mm_packus_epi16(tpl, tph); + ta = _mm_packus_epi16(ttb, tta); + + ti = _mm_and_si128 (mask, tb); + tph = _mm_shuffle_epi8 (thigh[0], ti); + tpl = _mm_shuffle_epi8 (tlow[0], ti); + + tb = _mm_srli_epi16(tb, 4); + ti = _mm_and_si128 (mask, tb); + tpl = _mm_xor_si128(_mm_shuffle_epi8 (tlow[1], ti), tpl); + tph = _mm_xor_si128(_mm_shuffle_epi8 (thigh[1], ti), tph); + + ti = _mm_and_si128 (mask, ta); + tpl = _mm_xor_si128(_mm_shuffle_epi8 (tlow[2], ti), tpl); + tph = _mm_xor_si128(_mm_shuffle_epi8 (thigh[2], ti), tph); + + ta = _mm_srli_epi16(ta, 4); + ti = _mm_and_si128 (mask, ta); + tpl = _mm_xor_si128(_mm_shuffle_epi8 (tlow[3], ti), tpl); + tph = _mm_xor_si128(_mm_shuffle_epi8 (thigh[3], ti), tph); + + ta = _mm_unpackhi_epi8(tpl, tph); + tb = _mm_unpacklo_epi8(tpl, tph); + + _mm_store_si128 ((__m128i *)d64, ta); + _mm_store_si128 ((__m128i *)(d64+2), tb); + + d64 += 4; + s64 += 4; + } + } + + gf_do_final_region_alignment(&rd); +} +#endif + +#ifdef INTEL_SSSE3 +static +void +gf_w16_split_4_16_lazy_sse_altmap_multiply_region(gf_t *gf, void *src, void *dest, gf_val_32_t val, int bytes, int xor) +{ + uint64_t i, j, *s64, *d64, *top64;; + uint64_t c, prod; + uint8_t low[4][16]; + uint8_t high[4][16]; + gf_region_data rd; + __m128i mask, ta, tb, ti, tpl, tph, tlow[4], thigh[4]; + + if (val == 0) { gf_multby_zero(dest, bytes, xor); return; } + if (val == 1) { gf_multby_one(src, dest, bytes, xor); return; } + + gf_set_region_data(&rd, gf, src, dest, bytes, val, xor, 32); + gf_do_initial_region_alignment(&rd); + + for (j = 0; j < 16; j++) { + for (i = 0; i < 4; i++) { + c = (j << (i*4)); + prod = gf->multiply.w32(gf, c, val); + low[i][j] = (prod & 0xff); + high[i][j] = (prod >> 8); + } + } + + for (i = 0; i < 4; i++) { + tlow[i] = _mm_loadu_si128((__m128i *)low[i]); + thigh[i] = _mm_loadu_si128((__m128i *)high[i]); + } + + s64 = (uint64_t *) rd.s_start; + d64 = (uint64_t *) rd.d_start; + top64 = (uint64_t *) rd.d_top; + + mask = _mm_set1_epi8 (0x0f); + + if (xor) { + while (d64 != top64) { + + ta = _mm_load_si128((__m128i *) s64); + tb = _mm_load_si128((__m128i *) (s64+2)); + + ti = _mm_and_si128 (mask, tb); + tph = _mm_shuffle_epi8 (thigh[0], ti); + tpl = _mm_shuffle_epi8 (tlow[0], ti); + + tb = _mm_srli_epi16(tb, 4); + ti = _mm_and_si128 (mask, tb); + tpl = _mm_xor_si128(_mm_shuffle_epi8 (tlow[1], ti), tpl); + tph = _mm_xor_si128(_mm_shuffle_epi8 (thigh[1], ti), tph); + + ti = _mm_and_si128 (mask, ta); + tpl = _mm_xor_si128(_mm_shuffle_epi8 (tlow[2], ti), tpl); + tph = _mm_xor_si128(_mm_shuffle_epi8 (thigh[2], ti), tph); + + ta = _mm_srli_epi16(ta, 4); + ti = _mm_and_si128 (mask, ta); + tpl = _mm_xor_si128(_mm_shuffle_epi8 (tlow[3], ti), tpl); + tph = _mm_xor_si128(_mm_shuffle_epi8 (thigh[3], ti), tph); + + ta = _mm_load_si128((__m128i *) d64); + tph = _mm_xor_si128(tph, ta); + _mm_store_si128 ((__m128i *)d64, tph); + tb = _mm_load_si128((__m128i *) (d64+2)); + tpl = _mm_xor_si128(tpl, tb); + _mm_store_si128 ((__m128i *)(d64+2), tpl); + + d64 += 4; + s64 += 4; + } + } else { + while (d64 != top64) { + + ta = _mm_load_si128((__m128i *) s64); + tb = _mm_load_si128((__m128i *) (s64+2)); + + ti = _mm_and_si128 (mask, tb); + tph = _mm_shuffle_epi8 (thigh[0], ti); + tpl = _mm_shuffle_epi8 (tlow[0], ti); + + tb = _mm_srli_epi16(tb, 4); + ti = _mm_and_si128 (mask, tb); + tpl = _mm_xor_si128(_mm_shuffle_epi8 (tlow[1], ti), tpl); + tph = _mm_xor_si128(_mm_shuffle_epi8 (thigh[1], ti), tph); + + ti = _mm_and_si128 (mask, ta); + tpl = _mm_xor_si128(_mm_shuffle_epi8 (tlow[2], ti), tpl); + tph = _mm_xor_si128(_mm_shuffle_epi8 (thigh[2], ti), tph); + + ta = _mm_srli_epi16(ta, 4); + ti = _mm_and_si128 (mask, ta); + tpl = _mm_xor_si128(_mm_shuffle_epi8 (tlow[3], ti), tpl); + tph = _mm_xor_si128(_mm_shuffle_epi8 (thigh[3], ti), tph); + + _mm_store_si128 ((__m128i *)d64, tph); + _mm_store_si128 ((__m128i *)(d64+2), tpl); + + d64 += 4; + s64 += 4; + + } + } + gf_do_final_region_alignment(&rd); + +} +#endif + +uint32_t +gf_w16_split_8_8_multiply(gf_t *gf, gf_val_32_t a, gf_val_32_t b) +{ + uint32_t alow, blow; + struct gf_w16_split_8_8_data *d8; + gf_internal_t *h; + + h = (gf_internal_t *) gf->scratch; + d8 = (struct gf_w16_split_8_8_data *) h->private; + + alow = a & 0xff; + blow = b & 0xff; + a >>= 8; + b >>= 8; + + return d8->tables[0][alow][blow] ^ + d8->tables[1][alow][b] ^ + d8->tables[1][a][blow] ^ + d8->tables[2][a][b]; +} + +static +int gf_w16_split_init(gf_t *gf) +{ + gf_internal_t *h; + struct gf_w16_split_8_8_data *d8; + int i, j, exp; + uint32_t p, basep, tmp; + + h = (gf_internal_t *) gf->scratch; + + if (h->arg1 == 8 && h->arg2 == 8) { + d8 = (struct gf_w16_split_8_8_data *) h->private; + basep = 1; + for (exp = 0; exp < 3; exp++) { + for (j = 0; j < 256; j++) d8->tables[exp][0][j] = 0; + for (i = 0; i < 256; i++) d8->tables[exp][i][0] = 0; + d8->tables[exp][1][1] = basep; + for (i = 2; i < 256; i++) { + if (i&1) { + p = d8->tables[exp][i^1][1]; + d8->tables[exp][i][1] = p ^ basep; + } else { + p = d8->tables[exp][i>>1][1]; + d8->tables[exp][i][1] = GF_MULTBY_TWO(p); + } + } + for (i = 1; i < 256; i++) { + p = d8->tables[exp][i][1]; + for (j = 1; j < 256; j++) { + if (j&1) { + d8->tables[exp][i][j] = d8->tables[exp][i][j^1] ^ p; + } else { + tmp = d8->tables[exp][i][j>>1]; + d8->tables[exp][i][j] = GF_MULTBY_TWO(tmp); + } + } + } + for (i = 0; i < 8; i++) basep = GF_MULTBY_TWO(basep); + } + SET_FUNCTION(gf,multiply,w32,gf_w16_split_8_8_multiply) + SET_FUNCTION(gf,multiply_region,w32,gf_w16_split_8_16_lazy_multiply_region) + return 1; + + } + + /* We'll be using LOG for multiplication, unless the pp isn't primitive. + In that case, we'll be using SHIFT. */ + + gf_w16_log_init(gf); + + /* Defaults */ + +#ifdef INTEL_SSSE3 + if (gf_cpu_supports_intel_ssse3) { + SET_FUNCTION(gf,multiply_region,w32,gf_w16_split_4_16_lazy_sse_multiply_region) + } else { +#elif ARM_NEON + if (gf_cpu_supports_arm_neon) { + gf_w16_neon_split_init(gf); + } else { +#endif + SET_FUNCTION(gf,multiply_region,w32,gf_w16_split_8_16_lazy_multiply_region) +#if defined(INTEL_SSSE3) || defined(ARM_NEON) + } +#endif + + if ((h->arg1 == 8 && h->arg2 == 16) || (h->arg2 == 8 && h->arg1 == 16)) { + SET_FUNCTION(gf,multiply_region,w32,gf_w16_split_8_16_lazy_multiply_region) + + } else if ((h->arg1 == 4 && h->arg2 == 16) || (h->arg2 == 4 && h->arg1 == 16)) { +#if defined(INTEL_SSSE3) || defined(ARM_NEON) + if (gf_cpu_supports_intel_ssse3 || gf_cpu_supports_arm_neon) { + if(h->region_type & GF_REGION_ALTMAP && h->region_type & GF_REGION_NOSIMD) + SET_FUNCTION(gf,multiply_region,w32,gf_w16_split_4_16_lazy_nosse_altmap_multiply_region) + else if(h->region_type & GF_REGION_NOSIMD) + SET_FUNCTION(gf,multiply_region,w32,gf_w16_split_4_16_lazy_multiply_region) +#if defined(INTEL_SSSE3) + else if(h->region_type & GF_REGION_ALTMAP && gf_cpu_supports_intel_ssse3) + SET_FUNCTION(gf,multiply_region,w32,gf_w16_split_4_16_lazy_sse_altmap_multiply_region) +#endif + } else { +#endif + if(h->region_type & GF_REGION_SIMD) + return 0; + else if(h->region_type & GF_REGION_ALTMAP) + SET_FUNCTION(gf,multiply_region,w32,gf_w16_split_4_16_lazy_nosse_altmap_multiply_region) + else + SET_FUNCTION(gf,multiply_region,w32,gf_w16_split_4_16_lazy_multiply_region) +#if defined(INTEL_SSSE3) || defined(ARM_NEON) + } +#endif + } + + return 1; +} + +static +int gf_w16_table_init(gf_t *gf) +{ + gf_w16_log_init(gf); + + SET_FUNCTION(gf,multiply_region,w32,gf_w16_table_lazy_multiply_region) + return 1; +} + +static +void +gf_w16_log_zero_multiply_region(gf_t *gf, void *src, void *dest, gf_val_32_t val, int bytes, int xor) +{ + uint16_t lv; + int i; + uint16_t *s16, *d16, *top16; + struct gf_w16_zero_logtable_data *ltd; + gf_region_data rd; + + if (val == 0) { gf_multby_zero(dest, bytes, xor); return; } + if (val == 1) { gf_multby_one(src, dest, bytes, xor); return; } + + gf_set_region_data(&rd, gf, src, dest, bytes, val, xor, 2); + gf_do_initial_region_alignment(&rd); + + ltd = (struct gf_w16_zero_logtable_data*) ((gf_internal_t *) gf->scratch)->private; + s16 = (uint16_t *) rd.s_start; + d16 = (uint16_t *) rd.d_start; + top16 = (uint16_t *) rd.d_top; + bytes = top16 - d16; + + lv = ltd->log_tbl[val]; + + if (xor) { + for (i = 0; i < bytes; i++) { + d16[i] ^= (ltd->antilog_tbl[lv + ltd->log_tbl[s16[i]]]); + } + } else { + for (i = 0; i < bytes; i++) { + d16[i] = (ltd->antilog_tbl[lv + ltd->log_tbl[s16[i]]]); + } + } + + /* This isn't necessary. */ + + gf_do_final_region_alignment(&rd); +} + +/* Here -- double-check Kevin */ + +static +inline +gf_val_32_t +gf_w16_log_zero_multiply (gf_t *gf, gf_val_32_t a, gf_val_32_t b) +{ + struct gf_w16_zero_logtable_data *ltd; + + ltd = (struct gf_w16_zero_logtable_data *) ((gf_internal_t *) gf->scratch)->private; + return ltd->antilog_tbl[ltd->log_tbl[a] + ltd->log_tbl[b]]; +} + +static +inline +gf_val_32_t +gf_w16_log_zero_divide (gf_t *gf, gf_val_32_t a, gf_val_32_t b) +{ + int log_sum = 0; + struct gf_w16_zero_logtable_data *ltd; + + if (a == 0 || b == 0) return 0; + ltd = (struct gf_w16_zero_logtable_data *) ((gf_internal_t *) gf->scratch)->private; + + log_sum = ltd->log_tbl[a] - ltd->log_tbl[b] + (GF_MULT_GROUP_SIZE); + return (ltd->antilog_tbl[log_sum]); +} + +static +gf_val_32_t +gf_w16_log_zero_inverse (gf_t *gf, gf_val_32_t a) +{ + struct gf_w16_zero_logtable_data *ltd; + + ltd = (struct gf_w16_zero_logtable_data *) ((gf_internal_t *) gf->scratch)->private; + return (ltd->inv_tbl[a]); +} + +static +inline +gf_val_32_t +gf_w16_bytwo_p_multiply (gf_t *gf, gf_val_32_t a, gf_val_32_t b) +{ + uint32_t prod, pp, pmask, amask; + gf_internal_t *h; + + h = (gf_internal_t *) gf->scratch; + pp = h->prim_poly; + + + prod = 0; + pmask = 0x8000; + amask = 0x8000; + + while (amask != 0) { + if (prod & pmask) { + prod = ((prod << 1) ^ pp); + } else { + prod <<= 1; + } + if (a & amask) prod ^= b; + amask >>= 1; + } + return prod; +} + +static +inline +gf_val_32_t +gf_w16_bytwo_b_multiply (gf_t *gf, gf_val_32_t a, gf_val_32_t b) +{ + uint32_t prod, pp, bmask; + gf_internal_t *h; + + h = (gf_internal_t *) gf->scratch; + pp = h->prim_poly; + + prod = 0; + bmask = 0x8000; + + while (1) { + if (a & 1) prod ^= b; + a >>= 1; + if (a == 0) return prod; + if (b & bmask) { + b = ((b << 1) ^ pp); + } else { + b <<= 1; + } + } +} + +static +void +gf_w16_bytwo_p_nosse_multiply_region(gf_t *gf, void *src, void *dest, gf_val_32_t val, int bytes, int xor) +{ + uint64_t *s64, *d64, t1, t2, ta, prod, amask; + gf_region_data rd; + struct gf_w16_bytwo_data *btd; + + if (val == 0) { gf_multby_zero(dest, bytes, xor); return; } + if (val == 1) { gf_multby_one(src, dest, bytes, xor); return; } + + btd = (struct gf_w16_bytwo_data *) ((gf_internal_t *) (gf->scratch))->private; + + gf_set_region_data(&rd, gf, src, dest, bytes, val, xor, 8); + gf_do_initial_region_alignment(&rd); + + s64 = (uint64_t *) rd.s_start; + d64 = (uint64_t *) rd.d_start; + + if (xor) { + while (s64 < (uint64_t *) rd.s_top) { + prod = 0; + amask = 0x8000; + ta = *s64; + while (amask != 0) { + AB2(btd->prim_poly, btd->mask1, btd->mask2, prod, t1, t2); + if (val & amask) prod ^= ta; + amask >>= 1; + } + *d64 ^= prod; + d64++; + s64++; + } + } else { + while (s64 < (uint64_t *) rd.s_top) { + prod = 0; + amask = 0x8000; + ta = *s64; + while (amask != 0) { + AB2(btd->prim_poly, btd->mask1, btd->mask2, prod, t1, t2); + if (val & amask) prod ^= ta; + amask >>= 1; + } + *d64 = prod; + d64++; + s64++; + } + } + gf_do_final_region_alignment(&rd); +} + +#define BYTWO_P_ONESTEP {\ + SSE_AB2(pp, m1 ,m2, prod, t1, t2); \ + t1 = _mm_and_si128(v, one); \ + t1 = _mm_sub_epi16(t1, one); \ + t1 = _mm_and_si128(t1, ta); \ + prod = _mm_xor_si128(prod, t1); \ + v = _mm_srli_epi64(v, 1); } + +#ifdef INTEL_SSE2 +static +void +gf_w16_bytwo_p_sse_multiply_region(gf_t *gf, void *src, void *dest, gf_val_32_t val, int bytes, int xor) +{ + int i; + uint8_t *s8, *d8; + uint32_t vrev; + __m128i pp, m1, m2, ta, prod, t1, t2, tp, one, v; + struct gf_w16_bytwo_data *btd; + gf_region_data rd; + + if (val == 0) { gf_multby_zero(dest, bytes, xor); return; } + if (val == 1) { gf_multby_one(src, dest, bytes, xor); return; } + + btd = (struct gf_w16_bytwo_data *) ((gf_internal_t *) (gf->scratch))->private; + + gf_set_region_data(&rd, gf, src, dest, bytes, val, xor, 16); + gf_do_initial_region_alignment(&rd); + + vrev = 0; + for (i = 0; i < 16; i++) { + vrev <<= 1; + if (!(val & (1 << i))) vrev |= 1; + } + + s8 = (uint8_t *) rd.s_start; + d8 = (uint8_t *) rd.d_start; + + pp = _mm_set1_epi16(btd->prim_poly&0xffff); + m1 = _mm_set1_epi16((btd->mask1)&0xffff); + m2 = _mm_set1_epi16((btd->mask2)&0xffff); + one = _mm_set1_epi16(1); + + while (d8 < (uint8_t *) rd.d_top) { + prod = _mm_setzero_si128(); + v = _mm_set1_epi16(vrev); + ta = _mm_load_si128((__m128i *) s8); + tp = (!xor) ? _mm_setzero_si128() : _mm_load_si128((__m128i *) d8); + BYTWO_P_ONESTEP; + BYTWO_P_ONESTEP; + BYTWO_P_ONESTEP; + BYTWO_P_ONESTEP; + BYTWO_P_ONESTEP; + BYTWO_P_ONESTEP; + BYTWO_P_ONESTEP; + BYTWO_P_ONESTEP; + BYTWO_P_ONESTEP; + BYTWO_P_ONESTEP; + BYTWO_P_ONESTEP; + BYTWO_P_ONESTEP; + BYTWO_P_ONESTEP; + BYTWO_P_ONESTEP; + BYTWO_P_ONESTEP; + BYTWO_P_ONESTEP; + _mm_store_si128((__m128i *) d8, _mm_xor_si128(prod, tp)); + d8 += 16; + s8 += 16; + } + gf_do_final_region_alignment(&rd); +} +#endif + +#ifdef INTEL_SSE2 +static +void +gf_w16_bytwo_b_sse_region_2_noxor(gf_region_data *rd, struct gf_w16_bytwo_data *btd) +{ + uint8_t *d8, *s8; + __m128i pp, m1, m2, t1, t2, va; + + s8 = (uint8_t *) rd->s_start; + d8 = (uint8_t *) rd->d_start; + + pp = _mm_set1_epi16(btd->prim_poly&0xffff); + m1 = _mm_set1_epi16((btd->mask1)&0xffff); + m2 = _mm_set1_epi16((btd->mask2)&0xffff); + + while (d8 < (uint8_t *) rd->d_top) { + va = _mm_load_si128 ((__m128i *)(s8)); + SSE_AB2(pp, m1, m2, va, t1, t2); + _mm_store_si128((__m128i *)d8, va); + d8 += 16; + s8 += 16; + } +} +#endif + +#ifdef INTEL_SSE2 +static +void +gf_w16_bytwo_b_sse_region_2_xor(gf_region_data *rd, struct gf_w16_bytwo_data *btd) +{ + uint8_t *d8, *s8; + __m128i pp, m1, m2, t1, t2, va, vb; + + s8 = (uint8_t *) rd->s_start; + d8 = (uint8_t *) rd->d_start; + + pp = _mm_set1_epi16(btd->prim_poly&0xffff); + m1 = _mm_set1_epi16((btd->mask1)&0xffff); + m2 = _mm_set1_epi16((btd->mask2)&0xffff); + + while (d8 < (uint8_t *) rd->d_top) { + va = _mm_load_si128 ((__m128i *)(s8)); + SSE_AB2(pp, m1, m2, va, t1, t2); + vb = _mm_load_si128 ((__m128i *)(d8)); + vb = _mm_xor_si128(vb, va); + _mm_store_si128((__m128i *)d8, vb); + d8 += 16; + s8 += 16; + } +} +#endif + + +#ifdef INTEL_SSE2 +static +void +gf_w16_bytwo_b_sse_multiply_region(gf_t *gf, void *src, void *dest, gf_val_32_t val, int bytes, int xor) +{ + int itb; + uint8_t *d8, *s8; + __m128i pp, m1, m2, t1, t2, va, vb; + struct gf_w16_bytwo_data *btd; + gf_region_data rd; + + if (val == 0) { gf_multby_zero(dest, bytes, xor); return; } + if (val == 1) { gf_multby_one(src, dest, bytes, xor); return; } + + gf_set_region_data(&rd, gf, src, dest, bytes, val, xor, 16); + gf_do_initial_region_alignment(&rd); + + btd = (struct gf_w16_bytwo_data *) ((gf_internal_t *) (gf->scratch))->private; + + if (val == 2) { + if (xor) { + gf_w16_bytwo_b_sse_region_2_xor(&rd, btd); + } else { + gf_w16_bytwo_b_sse_region_2_noxor(&rd, btd); + } + gf_do_final_region_alignment(&rd); + return; + } + + s8 = (uint8_t *) rd.s_start; + d8 = (uint8_t *) rd.d_start; + + pp = _mm_set1_epi16(btd->prim_poly&0xffff); + m1 = _mm_set1_epi16((btd->mask1)&0xffff); + m2 = _mm_set1_epi16((btd->mask2)&0xffff); + + while (d8 < (uint8_t *) rd.d_top) { + va = _mm_load_si128 ((__m128i *)(s8)); + vb = (!xor) ? _mm_setzero_si128() : _mm_load_si128 ((__m128i *)(d8)); + itb = val; + while (1) { + if (itb & 1) vb = _mm_xor_si128(vb, va); + itb >>= 1; + if (itb == 0) break; + SSE_AB2(pp, m1, m2, va, t1, t2); + } + _mm_store_si128((__m128i *)d8, vb); + d8 += 16; + s8 += 16; + } + + gf_do_final_region_alignment(&rd); +} +#endif + +static +void +gf_w16_bytwo_b_nosse_multiply_region(gf_t *gf, void *src, void *dest, gf_val_32_t val, int bytes, int xor) +{ + uint64_t *s64, *d64, t1, t2, ta, tb, prod; + struct gf_w16_bytwo_data *btd; + gf_region_data rd; + + if (val == 0) { gf_multby_zero(dest, bytes, xor); return; } + if (val == 1) { gf_multby_one(src, dest, bytes, xor); return; } + + gf_set_region_data(&rd, gf, src, dest, bytes, val, xor, 16); + gf_do_initial_region_alignment(&rd); + + btd = (struct gf_w16_bytwo_data *) ((gf_internal_t *) (gf->scratch))->private; + s64 = (uint64_t *) rd.s_start; + d64 = (uint64_t *) rd.d_start; + + switch (val) { + case 2: + if (xor) { + while (d64 < (uint64_t *) rd.d_top) { + ta = *s64; + AB2(btd->prim_poly, btd->mask1, btd->mask2, ta, t1, t2); + *d64 ^= ta; + d64++; + s64++; + } + } else { + while (d64 < (uint64_t *) rd.d_top) { + ta = *s64; + AB2(btd->prim_poly, btd->mask1, btd->mask2, ta, t1, t2); + *d64 = ta; + d64++; + s64++; + } + } + break; + case 3: + if (xor) { + while (d64 < (uint64_t *) rd.d_top) { + ta = *s64; + prod = ta; + AB2(btd->prim_poly, btd->mask1, btd->mask2, ta, t1, t2); + *d64 ^= (ta ^ prod); + d64++; + s64++; + } + } else { + while (d64 < (uint64_t *) rd.d_top) { + ta = *s64; + prod = ta; + AB2(btd->prim_poly, btd->mask1, btd->mask2, ta, t1, t2); + *d64 = (ta ^ prod); + d64++; + s64++; + } + } + break; + case 4: + if (xor) { + while (d64 < (uint64_t *) rd.d_top) { + ta = *s64; + AB2(btd->prim_poly, btd->mask1, btd->mask2, ta, t1, t2); + AB2(btd->prim_poly, btd->mask1, btd->mask2, ta, t1, t2); + *d64 ^= ta; + d64++; + s64++; + } + } else { + while (d64 < (uint64_t *) rd.d_top) { + ta = *s64; + AB2(btd->prim_poly, btd->mask1, btd->mask2, ta, t1, t2); + AB2(btd->prim_poly, btd->mask1, btd->mask2, ta, t1, t2); + *d64 = ta; + d64++; + s64++; + } + } + break; + case 5: + if (xor) { + while (d64 < (uint64_t *) rd.d_top) { + ta = *s64; + prod = ta; + AB2(btd->prim_poly, btd->mask1, btd->mask2, ta, t1, t2); + AB2(btd->prim_poly, btd->mask1, btd->mask2, ta, t1, t2); + *d64 ^= (ta ^ prod); + d64++; + s64++; + } + } else { + while (d64 < (uint64_t *) rd.d_top) { + ta = *s64; + prod = ta; + AB2(btd->prim_poly, btd->mask1, btd->mask2, ta, t1, t2); + AB2(btd->prim_poly, btd->mask1, btd->mask2, ta, t1, t2); + *d64 = ta ^ prod; + d64++; + s64++; + } + } + break; + default: + if (xor) { + while (d64 < (uint64_t *) rd.d_top) { + prod = *d64 ; + ta = *s64; + tb = val; + while (1) { + if (tb & 1) prod ^= ta; + tb >>= 1; + if (tb == 0) break; + AB2(btd->prim_poly, btd->mask1, btd->mask2, ta, t1, t2); + } + *d64 = prod; + d64++; + s64++; + } + } else { + while (d64 < (uint64_t *) rd.d_top) { + prod = 0 ; + ta = *s64; + tb = val; + while (1) { + if (tb & 1) prod ^= ta; + tb >>= 1; + if (tb == 0) break; + AB2(btd->prim_poly, btd->mask1, btd->mask2, ta, t1, t2); + } + *d64 = prod; + d64++; + s64++; + } + } + break; + } + gf_do_final_region_alignment(&rd); +} + +static +int gf_w16_bytwo_init(gf_t *gf) +{ + gf_internal_t *h; + uint64_t ip, m1, m2; + struct gf_w16_bytwo_data *btd; + + h = (gf_internal_t *) gf->scratch; + btd = (struct gf_w16_bytwo_data *) (h->private); + ip = h->prim_poly & 0xffff; + m1 = 0xfffe; + m2 = 0x8000; + btd->prim_poly = 0; + btd->mask1 = 0; + btd->mask2 = 0; + + while (ip != 0) { + btd->prim_poly |= ip; + btd->mask1 |= m1; + btd->mask2 |= m2; + ip <<= GF_FIELD_WIDTH; + m1 <<= GF_FIELD_WIDTH; + m2 <<= GF_FIELD_WIDTH; + } + + if (h->mult_type == GF_MULT_BYTWO_p) { + SET_FUNCTION(gf,multiply,w32,gf_w16_bytwo_p_multiply) + #ifdef INTEL_SSE2 + if (gf_cpu_supports_intel_sse2 && !(h->region_type & GF_REGION_NOSIMD)) { + SET_FUNCTION(gf,multiply_region,w32,gf_w16_bytwo_p_sse_multiply_region) + } else { + #endif + SET_FUNCTION(gf,multiply_region,w32,gf_w16_bytwo_p_nosse_multiply_region) + if(h->region_type & GF_REGION_SIMD) + return 0; + #ifdef INTEL_SSE2 + } + #endif + } else { + SET_FUNCTION(gf,multiply,w32,gf_w16_bytwo_b_multiply) + #ifdef INTEL_SSE2 + if (gf_cpu_supports_intel_sse2 && !(h->region_type & GF_REGION_NOSIMD)) { + SET_FUNCTION(gf,multiply_region,w32,gf_w16_bytwo_b_sse_multiply_region) + } else { + #endif + SET_FUNCTION(gf,multiply_region,w32,gf_w16_bytwo_b_nosse_multiply_region) + if(h->region_type & GF_REGION_SIMD) + return 0; + #ifdef INTEL_SSE2 + } + #endif + } + + return 1; +} + +static +int gf_w16_log_zero_init(gf_t *gf) +{ + gf_internal_t *h; + struct gf_w16_zero_logtable_data *ltd; + int i, b; + + h = (gf_internal_t *) gf->scratch; + ltd = h->private; + + ltd->log_tbl[0] = (-GF_MULT_GROUP_SIZE) + 1; + + bzero(&(ltd->_antilog_tbl[0]), sizeof(ltd->_antilog_tbl)); + + ltd->antilog_tbl = &(ltd->_antilog_tbl[GF_FIELD_SIZE * 2]); + + b = 1; + for (i = 0; i < GF_MULT_GROUP_SIZE; i++) { + ltd->log_tbl[b] = (uint16_t)i; + ltd->antilog_tbl[i] = (uint16_t)b; + ltd->antilog_tbl[i+GF_MULT_GROUP_SIZE] = (uint16_t)b; + b <<= 1; + if (b & GF_FIELD_SIZE) { + b = b ^ h->prim_poly; + } + } + ltd->inv_tbl[0] = 0; /* Not really, but we need to fill it with something */ + ltd->inv_tbl[1] = 1; + for (i = 2; i < GF_FIELD_SIZE; i++) { + ltd->inv_tbl[i] = ltd->antilog_tbl[GF_MULT_GROUP_SIZE-ltd->log_tbl[i]]; + } + + SET_FUNCTION(gf,inverse,w32,gf_w16_log_zero_inverse) + SET_FUNCTION(gf,divide,w32,gf_w16_log_zero_divide) + SET_FUNCTION(gf,multiply,w32,gf_w16_log_zero_multiply) + SET_FUNCTION(gf,multiply_region,w32,gf_w16_log_zero_multiply_region) + return 1; +} + +static +gf_val_32_t +gf_w16_composite_multiply_recursive(gf_t *gf, gf_val_32_t a, gf_val_32_t b) +{ + gf_internal_t *h = (gf_internal_t *) gf->scratch; + gf_t *base_gf = h->base_gf; + uint8_t b0 = b & 0x00ff; + uint8_t b1 = (b & 0xff00) >> 8; + uint8_t a0 = a & 0x00ff; + uint8_t a1 = (a & 0xff00) >> 8; + uint8_t a1b1; + uint16_t rv; + + a1b1 = base_gf->multiply.w32(base_gf, a1, b1); + + rv = ((base_gf->multiply.w32(base_gf, a0, b0) ^ a1b1) | ((base_gf->multiply.w32(base_gf, a1, b0) ^ base_gf->multiply.w32(base_gf, a0, b1) ^ base_gf->multiply.w32(base_gf, a1b1, h->prim_poly)) << 8)); + return rv; +} + +static +gf_val_32_t +gf_w16_composite_multiply_inline(gf_t *gf, gf_val_32_t a, gf_val_32_t b) +{ + gf_internal_t *h = (gf_internal_t *) gf->scratch; + uint8_t b0 = b & 0x00ff; + uint8_t b1 = (b & 0xff00) >> 8; + uint8_t a0 = a & 0x00ff; + uint8_t a1 = (a & 0xff00) >> 8; + uint8_t a1b1, *mt; + uint16_t rv; + struct gf_w16_composite_data *cd; + + cd = (struct gf_w16_composite_data *) h->private; + mt = cd->mult_table; + + a1b1 = GF_W8_INLINE_MULTDIV(mt, a1, b1); + + rv = ((GF_W8_INLINE_MULTDIV(mt, a0, b0) ^ a1b1) | ((GF_W8_INLINE_MULTDIV(mt, a1, b0) ^ GF_W8_INLINE_MULTDIV(mt, a0, b1) ^ GF_W8_INLINE_MULTDIV(mt, a1b1, h->prim_poly)) << 8)); + return rv; +} + +/* + * Composite field division trick (explained in 2007 tech report) + * + * Compute a / b = a*b^-1, where p(x) = x^2 + sx + 1 + * + * let c = b^-1 + * + * c*b = (s*b1c1+b1c0+b0c1)x+(b1c1+b0c0) + * + * want (s*b1c1+b1c0+b0c1) = 0 and (b1c1+b0c0) = 1 + * + * let d = b1c1 and d+1 = b0c0 + * + * solve s*b1c1+b1c0+b0c1 = 0 + * + * solution: d = (b1b0^-1)(b1b0^-1+b0b1^-1+s)^-1 + * + * c0 = (d+1)b0^-1 + * c1 = d*b1^-1 + * + * a / b = a * c + */ + +static +gf_val_32_t +gf_w16_composite_inverse(gf_t *gf, gf_val_32_t a) +{ + gf_internal_t *h = (gf_internal_t *) gf->scratch; + gf_t *base_gf = h->base_gf; + uint8_t a0 = a & 0x00ff; + uint8_t a1 = (a & 0xff00) >> 8; + uint8_t c0, c1, d, tmp; + uint16_t c; + uint8_t a0inv, a1inv; + + if (a0 == 0) { + a1inv = base_gf->inverse.w32(base_gf, a1); + c0 = base_gf->multiply.w32(base_gf, a1inv, h->prim_poly); + c1 = a1inv; + } else if (a1 == 0) { + c0 = base_gf->inverse.w32(base_gf, a0); + c1 = 0; + } else { + a1inv = base_gf->inverse.w32(base_gf, a1); + a0inv = base_gf->inverse.w32(base_gf, a0); + + d = base_gf->multiply.w32(base_gf, a1, a0inv); + + tmp = (base_gf->multiply.w32(base_gf, a1, a0inv) ^ base_gf->multiply.w32(base_gf, a0, a1inv) ^ h->prim_poly); + tmp = base_gf->inverse.w32(base_gf, tmp); + + d = base_gf->multiply.w32(base_gf, d, tmp); + + c0 = base_gf->multiply.w32(base_gf, (d^1), a0inv); + c1 = base_gf->multiply.w32(base_gf, d, a1inv); + } + + c = c0 | (c1 << 8); + + return c; +} + +static +void +gf_w16_composite_multiply_region(gf_t *gf, void *src, void *dest, gf_val_32_t val, int bytes, int xor) +{ + gf_internal_t *h = (gf_internal_t *) gf->scratch; + gf_t *base_gf = h->base_gf; + uint8_t b0 = val & 0x00ff; + uint8_t b1 = (val & 0xff00) >> 8; + uint16_t *s16, *d16, *top; + uint8_t a0, a1, a1b1, *mt; + gf_region_data rd; + struct gf_w16_composite_data *cd; + + cd = (struct gf_w16_composite_data *) h->private; + mt = cd->mult_table; + + if (val == 0) { gf_multby_zero(dest, bytes, xor); return; } + gf_set_region_data(&rd, gf, src, dest, bytes, val, xor, 2); + + s16 = rd.s_start; + d16 = rd.d_start; + top = rd.d_top; + + if (mt == NULL) { + if (xor) { + while (d16 < top) { + a0 = (*s16) & 0x00ff; + a1 = ((*s16) & 0xff00) >> 8; + a1b1 = base_gf->multiply.w32(base_gf, a1, b1); + + (*d16) ^= ((base_gf->multiply.w32(base_gf, a0, b0) ^ a1b1) | + ((base_gf->multiply.w32(base_gf, a1, b0) ^ + base_gf->multiply.w32(base_gf, a0, b1) ^ + base_gf->multiply.w32(base_gf, a1b1, h->prim_poly)) << 8)); + s16++; + d16++; + } + } else { + while (d16 < top) { + a0 = (*s16) & 0x00ff; + a1 = ((*s16) & 0xff00) >> 8; + a1b1 = base_gf->multiply.w32(base_gf, a1, b1); + + (*d16) = ((base_gf->multiply.w32(base_gf, a0, b0) ^ a1b1) | + ((base_gf->multiply.w32(base_gf, a1, b0) ^ + base_gf->multiply.w32(base_gf, a0, b1) ^ + base_gf->multiply.w32(base_gf, a1b1, h->prim_poly)) << 8)); + s16++; + d16++; + } + } + } else { + if (xor) { + while (d16 < top) { + a0 = (*s16) & 0x00ff; + a1 = ((*s16) & 0xff00) >> 8; + a1b1 = GF_W8_INLINE_MULTDIV(mt, a1, b1); + + (*d16) ^= ((GF_W8_INLINE_MULTDIV(mt, a0, b0) ^ a1b1) | + ((GF_W8_INLINE_MULTDIV(mt, a1, b0) ^ + GF_W8_INLINE_MULTDIV(mt, a0, b1) ^ + GF_W8_INLINE_MULTDIV(mt, a1b1, h->prim_poly)) << 8)); + s16++; + d16++; + } + } else { + while (d16 < top) { + a0 = (*s16) & 0x00ff; + a1 = ((*s16) & 0xff00) >> 8; + a1b1 = GF_W8_INLINE_MULTDIV(mt, a1, b1); + + (*d16) = ((GF_W8_INLINE_MULTDIV(mt, a0, b0) ^ a1b1) | + ((GF_W8_INLINE_MULTDIV(mt, a1, b0) ^ + GF_W8_INLINE_MULTDIV(mt, a0, b1) ^ + GF_W8_INLINE_MULTDIV(mt, a1b1, h->prim_poly)) << 8)); + s16++; + d16++; + } + } + } +} + +static +void +gf_w16_composite_multiply_region_alt(gf_t *gf, void *src, void *dest, gf_val_32_t val, int bytes, int xor) +{ + gf_internal_t *h = (gf_internal_t *) gf->scratch; + gf_t *base_gf = h->base_gf; + uint8_t val0 = val & 0x00ff; + uint8_t val1 = (val & 0xff00) >> 8; + gf_region_data rd; + int sub_reg_size; + uint8_t *slow, *shigh; + uint8_t *dlow, *dhigh, *top;; + + /* JSP: I want the two pointers aligned wrt each other on 16 byte + boundaries. So I'm going to make sure that the area on + which the two operate is a multiple of 32. Of course, that + junks up the mapping, but so be it -- that's why we have extract_word.... */ + + gf_set_region_data(&rd, gf, src, dest, bytes, val, xor, 32); + gf_do_initial_region_alignment(&rd); + + slow = (uint8_t *) rd.s_start; + dlow = (uint8_t *) rd.d_start; + top = (uint8_t *) rd.d_top; + sub_reg_size = (top - dlow)/2; + shigh = slow + sub_reg_size; + dhigh = dlow + sub_reg_size; + + base_gf->multiply_region.w32(base_gf, slow, dlow, val0, sub_reg_size, xor); + base_gf->multiply_region.w32(base_gf, shigh, dlow, val1, sub_reg_size, 1); + base_gf->multiply_region.w32(base_gf, slow, dhigh, val1, sub_reg_size, xor); + base_gf->multiply_region.w32(base_gf, shigh, dhigh, val0, sub_reg_size, 1); + base_gf->multiply_region.w32(base_gf, shigh, dhigh, base_gf->multiply.w32(base_gf, h->prim_poly, val1), sub_reg_size, 1); + + gf_do_final_region_alignment(&rd); +} + +static +int gf_w16_composite_init(gf_t *gf) +{ + gf_internal_t *h = (gf_internal_t *) gf->scratch; + struct gf_w16_composite_data *cd; + + if (h->base_gf == NULL) return 0; + + cd = (struct gf_w16_composite_data *) h->private; + cd->mult_table = gf_w8_get_mult_table(h->base_gf); + + if (h->region_type & GF_REGION_ALTMAP) { + SET_FUNCTION(gf,multiply_region,w32,gf_w16_composite_multiply_region_alt) + } else { + SET_FUNCTION(gf,multiply_region,w32,gf_w16_composite_multiply_region) + } + + if (cd->mult_table == NULL) { + SET_FUNCTION(gf,multiply,w32,gf_w16_composite_multiply_recursive) + } else { + SET_FUNCTION(gf,multiply,w32,gf_w16_composite_multiply_inline) + } + SET_FUNCTION(gf,divide,w32,NULL) + SET_FUNCTION(gf,inverse,w32,gf_w16_composite_inverse) + + return 1; +} + +static +void +gf_w16_group_4_set_shift_tables(uint16_t *shift, uint16_t val, gf_internal_t *h) +{ + int i, j; + + shift[0] = 0; + for (i = 0; i < 16; i += 2) { + j = (shift[i>>1] << 1); + if (j & (1 << 16)) j ^= h->prim_poly; + shift[i] = j; + shift[i^1] = j^val; + } +} + +static +inline +gf_val_32_t +gf_w16_group_4_4_multiply(gf_t *gf, gf_val_32_t a, gf_val_32_t b) +{ + uint16_t p, l, ind, r, a16; + + struct gf_w16_group_4_4_data *d44; + gf_internal_t *h = (gf_internal_t *) gf->scratch; + + d44 = (struct gf_w16_group_4_4_data *) h->private; + gf_w16_group_4_set_shift_tables(d44->shift, b, h); + + a16 = a; + ind = a16 >> 12; + a16 <<= 4; + p = d44->shift[ind]; + r = p & 0xfff; + l = p >> 12; + ind = a16 >> 12; + a16 <<= 4; + p = (d44->shift[ind] ^ d44->reduce[l] ^ (r << 4)); + r = p & 0xfff; + l = p >> 12; + ind = a16 >> 12; + a16 <<= 4; + p = (d44->shift[ind] ^ d44->reduce[l] ^ (r << 4)); + r = p & 0xfff; + l = p >> 12; + ind = a16 >> 12; + p = (d44->shift[ind] ^ d44->reduce[l] ^ (r << 4)); + return p; +} + +static +void gf_w16_group_4_4_region_multiply(gf_t *gf, void *src, void *dest, gf_val_32_t val, int bytes, int xor) +{ + uint16_t p, l, ind, r, a16, p16; + struct gf_w16_group_4_4_data *d44; + gf_region_data rd; + uint16_t *s16, *d16, *top; + + if (val == 0) { gf_multby_zero(dest, bytes, xor); return; } + if (val == 1) { gf_multby_one(src, dest, bytes, xor); return; } + + gf_internal_t *h = (gf_internal_t *) gf->scratch; + d44 = (struct gf_w16_group_4_4_data *) h->private; + gf_w16_group_4_set_shift_tables(d44->shift, val, h); + + gf_set_region_data(&rd, gf, src, dest, bytes, val, xor, 2); + gf_do_initial_region_alignment(&rd); + + s16 = (uint16_t *) rd.s_start; + d16 = (uint16_t *) rd.d_start; + top = (uint16_t *) rd.d_top; + + while (d16 < top) { + a16 = *s16; + p16 = (xor) ? *d16 : 0; + ind = a16 >> 12; + a16 <<= 4; + p = d44->shift[ind]; + r = p & 0xfff; + l = p >> 12; + ind = a16 >> 12; + a16 <<= 4; + p = (d44->shift[ind] ^ d44->reduce[l] ^ (r << 4)); + r = p & 0xfff; + l = p >> 12; + ind = a16 >> 12; + a16 <<= 4; + p = (d44->shift[ind] ^ d44->reduce[l] ^ (r << 4)); + r = p & 0xfff; + l = p >> 12; + ind = a16 >> 12; + p = (d44->shift[ind] ^ d44->reduce[l] ^ (r << 4)); + p ^= p16; + *d16 = p; + d16++; + s16++; + } + gf_do_final_region_alignment(&rd); +} + +static +int gf_w16_group_init(gf_t *gf) +{ + int i, j, p; + struct gf_w16_group_4_4_data *d44; + gf_internal_t *h = (gf_internal_t *) gf->scratch; + + d44 = (struct gf_w16_group_4_4_data *) h->private; + d44->reduce[0] = 0; + for (i = 0; i < 16; i++) { + p = 0; + for (j = 0; j < 4; j++) { + if (i & (1 << j)) p ^= (h->prim_poly << j); + } + d44->reduce[p>>16] = (p&0xffff); + } + + SET_FUNCTION(gf,multiply,w32,gf_w16_group_4_4_multiply) + SET_FUNCTION(gf,divide,w32,NULL) + SET_FUNCTION(gf,inverse,w32,NULL) + SET_FUNCTION(gf,multiply_region,w32,gf_w16_group_4_4_region_multiply) + + return 1; +} + +int gf_w16_scratch_size(int mult_type, int region_type, int divide_type, int arg1, int arg2) +{ + switch(mult_type) + { + case GF_MULT_TABLE: + return sizeof(gf_internal_t) + sizeof(struct gf_w16_lazytable_data) + 64; + break; + case GF_MULT_BYTWO_p: + case GF_MULT_BYTWO_b: + return sizeof(gf_internal_t) + sizeof(struct gf_w16_bytwo_data); + break; + case GF_MULT_LOG_ZERO: + return sizeof(gf_internal_t) + sizeof(struct gf_w16_zero_logtable_data) + 64; + break; + case GF_MULT_LOG_TABLE: + return sizeof(gf_internal_t) + sizeof(struct gf_w16_logtable_data) + 64; + break; + case GF_MULT_DEFAULT: + case GF_MULT_SPLIT_TABLE: + if (arg1 == 8 && arg2 == 8) { + return sizeof(gf_internal_t) + sizeof(struct gf_w16_split_8_8_data) + 64; + } else if ((arg1 == 8 && arg2 == 16) || (arg2 == 8 && arg1 == 16)) { + return sizeof(gf_internal_t) + sizeof(struct gf_w16_logtable_data) + 64; + } else if (mult_type == GF_MULT_DEFAULT || + (arg1 == 4 && arg2 == 16) || (arg2 == 4 && arg1 == 16)) { + return sizeof(gf_internal_t) + sizeof(struct gf_w16_logtable_data) + 64; + } + return 0; + break; + case GF_MULT_GROUP: + return sizeof(gf_internal_t) + sizeof(struct gf_w16_group_4_4_data) + 64; + break; + case GF_MULT_CARRY_FREE: + return sizeof(gf_internal_t); + break; + case GF_MULT_SHIFT: + return sizeof(gf_internal_t); + break; + case GF_MULT_COMPOSITE: + return sizeof(gf_internal_t) + sizeof(struct gf_w16_composite_data) + 64; + break; + + default: + return 0; + } + return 0; +} + +int gf_w16_init(gf_t *gf) +{ + gf_internal_t *h; + + h = (gf_internal_t *) gf->scratch; + + /* Allen: set default primitive polynomial / irreducible polynomial if needed */ + + if (h->prim_poly == 0) { + if (h->mult_type == GF_MULT_COMPOSITE) { + h->prim_poly = gf_composite_get_default_poly(h->base_gf); + if (h->prim_poly == 0) return 0; + } else { + + /* Allen: use the following primitive polynomial to make + carryless multiply work more efficiently for GF(2^16). + + h->prim_poly = 0x1002d; + + The following is the traditional primitive polynomial for GF(2^16) */ + + h->prim_poly = 0x1100b; + } + } + + if (h->mult_type != GF_MULT_COMPOSITE) h->prim_poly |= (1 << 16); + + SET_FUNCTION(gf,multiply,w32,NULL) + SET_FUNCTION(gf,divide,w32,NULL) + SET_FUNCTION(gf,inverse,w32,NULL) + SET_FUNCTION(gf,multiply_region,w32,NULL) + + switch(h->mult_type) { + case GF_MULT_LOG_ZERO: if (gf_w16_log_zero_init(gf) == 0) return 0; break; + case GF_MULT_LOG_TABLE: if (gf_w16_log_init(gf) == 0) return 0; break; + case GF_MULT_DEFAULT: + case GF_MULT_SPLIT_TABLE: if (gf_w16_split_init(gf) == 0) return 0; break; + case GF_MULT_TABLE: if (gf_w16_table_init(gf) == 0) return 0; break; + case GF_MULT_CARRY_FREE: if (gf_w16_cfm_init(gf) == 0) return 0; break; + case GF_MULT_SHIFT: if (gf_w16_shift_init(gf) == 0) return 0; break; + case GF_MULT_COMPOSITE: if (gf_w16_composite_init(gf) == 0) return 0; break; + case GF_MULT_BYTWO_p: + case GF_MULT_BYTWO_b: if (gf_w16_bytwo_init(gf) == 0) return 0; break; + case GF_MULT_GROUP: if (gf_w16_group_init(gf) == 0) return 0; break; + default: return 0; + } + if (h->divide_type == GF_DIVIDE_EUCLID) { + SET_FUNCTION(gf,divide,w32,gf_w16_divide_from_inverse) + SET_FUNCTION(gf,inverse,w32,gf_w16_euclid) + } else if (h->divide_type == GF_DIVIDE_MATRIX) { + SET_FUNCTION(gf,divide,w32,gf_w16_divide_from_inverse) + SET_FUNCTION(gf,inverse,w32,gf_w16_matrix) + } + + if (gf->divide.w32 == NULL) { + SET_FUNCTION(gf,divide,w32,gf_w16_divide_from_inverse) + if (gf->inverse.w32 == NULL) SET_FUNCTION(gf,inverse,w32,gf_w16_euclid) + } + + if (gf->inverse.w32 == NULL) SET_FUNCTION(gf,inverse,w32,gf_w16_inverse_from_divide) + + if (h->region_type & GF_REGION_ALTMAP) { + if (h->mult_type == GF_MULT_COMPOSITE) { + SET_FUNCTION(gf,extract_word,w32,gf_w16_composite_extract_word) + } else { + SET_FUNCTION(gf,extract_word,w32,gf_w16_split_extract_word) + } + } else if (h->region_type == GF_REGION_CAUCHY) { + SET_FUNCTION(gf,multiply_region,w32,gf_wgen_cauchy_region) + SET_FUNCTION(gf,extract_word,w32,gf_wgen_extract_word) + } else { + SET_FUNCTION(gf,extract_word,w32,gf_w16_extract_word) + } + if (gf->multiply_region.w32 == NULL) { + SET_FUNCTION(gf,multiply_region,w32,gf_w16_multiply_region_from_single) + } + return 1; +} + +/* Inline setup functions */ + +uint16_t *gf_w16_get_log_table(gf_t *gf) +{ + struct gf_w16_logtable_data *ltd; + + if (gf->multiply.w32 == gf_w16_log_multiply) { + ltd = (struct gf_w16_logtable_data *) ((gf_internal_t *) gf->scratch)->private; + return (uint16_t *) ltd->log_tbl; + } + return NULL; +} + +uint16_t *gf_w16_get_mult_alog_table(gf_t *gf) +{ + gf_internal_t *h; + struct gf_w16_logtable_data *ltd; + + h = (gf_internal_t *) gf->scratch; + if (gf->multiply.w32 == gf_w16_log_multiply) { + ltd = (struct gf_w16_logtable_data *) h->private; + return (uint16_t *) ltd->antilog_tbl; + } + return NULL; +} + +uint16_t *gf_w16_get_div_alog_table(gf_t *gf) +{ + gf_internal_t *h; + struct gf_w16_logtable_data *ltd; + + h = (gf_internal_t *) gf->scratch; + if (gf->multiply.w32 == gf_w16_log_multiply) { + ltd = (struct gf_w16_logtable_data *) h->private; + return (uint16_t *) ltd->d_antilog; + } + return NULL; +} diff --git a/IDA_new/gf-complete/src/gf_w32.c b/IDA_new/gf-complete/src/gf_w32.c new file mode 100644 index 0000000..976b68b --- /dev/null +++ b/IDA_new/gf-complete/src/gf_w32.c @@ -0,0 +1,2810 @@ +/* + * GF-Complete: A Comprehensive Open Source Library for Galois Field Arithmetic + * James S. Plank, Ethan L. Miller, Kevin M. Greenan, + * Benjamin A. Arnold, John A. Burnum, Adam W. Disney, Allen C. McBride. + * + * gf_w32.c + * + * Routines for 32-bit Galois fields + */ + + +#include "gf_int.h" +#include +#include +#include "gf_w32.h" +#include "gf_cpu.h" + +#define MM_PRINT32(s, r) { uint8_t blah[16], ii; printf("%-12s", s); _mm_storeu_si128((__m128i *)blah, r); for (ii = 0; ii < 16; ii += 4) printf(" %02x%02x%02x%02x", blah[15-ii], blah[14-ii], blah[13-ii], blah[12-ii]); printf("\n"); } + +#define MM_PRINT8(s, r) { uint8_t blah[16], ii; printf("%-12s", s); _mm_storeu_si128((__m128i *)blah, r); for (ii = 0; ii < 16; ii += 1) printf("%s%02x", (ii%4==0) ? " " : " ", blah[15-ii]); printf("\n"); } + +#define AB2(ip, am1 ,am2, b, t1, t2) {\ + t1 = (b << 1) & am1;\ + t2 = b & am2; \ + t2 = ((t2 << 1) - (t2 >> (GF_FIELD_WIDTH-1))); \ + b = (t1 ^ (t2 & ip));} + +#define SSE_AB2(pp, m1 ,m2, va, t1, t2) {\ + t1 = _mm_and_si128(_mm_slli_epi64(va, 1), m1); \ + t2 = _mm_and_si128(va, m2); \ + t2 = _mm_sub_epi64 (_mm_slli_epi64(t2, 1), _mm_srli_epi64(t2, (GF_FIELD_WIDTH-1))); \ + va = _mm_xor_si128(t1, _mm_and_si128(t2, pp)); } + +static +inline +uint32_t gf_w32_inverse_from_divide (gf_t *gf, uint32_t a) +{ + return gf->divide.w32(gf, 1, a); +} + +static +inline +uint32_t gf_w32_divide_from_inverse (gf_t *gf, uint32_t a, uint32_t b) +{ + b = gf->inverse.w32(gf, b); + return gf->multiply.w32(gf, a, b); +} + +static +void +gf_w32_multiply_region_from_single(gf_t *gf, void *src, void *dest, uint32_t val, int bytes, int +xor) +{ + uint32_t i; + uint32_t *s32; + uint32_t *d32; + + s32 = (uint32_t *) src; + d32 = (uint32_t *) dest; + + if (xor) { + for (i = 0; i < bytes/sizeof(uint32_t); i++) { + d32[i] ^= gf->multiply.w32(gf, val, s32[i]); + } + } else { + for (i = 0; i < bytes/sizeof(uint32_t); i++) { + d32[i] = gf->multiply.w32(gf, val, s32[i]); + } + } +} + +#if defined(INTEL_SSE4_PCLMUL) + +static +void +gf_w32_clm_multiply_region_from_single_2(gf_t *gf, void *src, void *dest, uint32_t val, int bytes, int xor) +{ + + uint32_t i; + uint32_t *s32; + uint32_t *d32; + + __m128i a, b; + __m128i result; + __m128i prim_poly; + __m128i w; + gf_internal_t * h = gf->scratch; + + prim_poly = _mm_set_epi32(0, 0, 1, (uint32_t)(h->prim_poly & 0xffffffffULL)); + + if (val == 0) { gf_multby_zero(dest, bytes, xor); return; } + if (val == 1) { gf_multby_one(src, dest, bytes, xor); return; } + + a = _mm_insert_epi32 (_mm_setzero_si128(), val, 0); + s32 = (uint32_t *) src; + d32 = (uint32_t *) dest; + + if (xor) { + for (i = 0; i < bytes/sizeof(uint32_t); i++) { + b = _mm_insert_epi32 (a, s32[i], 0); + result = _mm_clmulepi64_si128 (a, b, 0); + w = _mm_clmulepi64_si128 (prim_poly, _mm_srli_si128 (result, 4), 0); + result = _mm_xor_si128 (result, w); + w = _mm_clmulepi64_si128 (prim_poly, _mm_srli_si128 (result, 4), 0); + result = _mm_xor_si128 (result, w); + d32[i] ^= ((gf_val_32_t)_mm_extract_epi32(result, 0)); + } + } else { + for (i = 0; i < bytes/sizeof(uint32_t); i++) { + b = _mm_insert_epi32 (a, s32[i], 0); + result = _mm_clmulepi64_si128 (a, b, 0); + w = _mm_clmulepi64_si128 (prim_poly, _mm_srli_si128 (result, 4), 0); + result = _mm_xor_si128 (result, w); + w = _mm_clmulepi64_si128 (prim_poly, _mm_srli_si128 (result, 4), 0); + result = _mm_xor_si128 (result, w); + d32[i] = ((gf_val_32_t)_mm_extract_epi32(result, 0)); + } + } +} +#endif + +#if defined(INTEL_SSE4_PCLMUL) + +static +void +gf_w32_clm_multiply_region_from_single_3(gf_t *gf, void *src, void *dest, uint32_t val, int bytes, int xor) +{ + + uint32_t i; + uint32_t *s32; + uint32_t *d32; + + __m128i a, b; + __m128i result; + __m128i prim_poly; + __m128i w; + gf_internal_t * h = gf->scratch; + + prim_poly = _mm_set_epi32(0, 0, 1, (uint32_t)(h->prim_poly & 0xffffffffULL)); + + if (val == 0) { gf_multby_zero(dest, bytes, xor); return; } + if (val == 1) { gf_multby_one(src, dest, bytes, xor); return; } + + a = _mm_insert_epi32 (_mm_setzero_si128(), val, 0); + + s32 = (uint32_t *) src; + d32 = (uint32_t *) dest; + + if (xor) { + for (i = 0; i < bytes/sizeof(uint32_t); i++) { + b = _mm_insert_epi32 (a, s32[i], 0); + result = _mm_clmulepi64_si128 (a, b, 0); + w = _mm_clmulepi64_si128 (prim_poly, _mm_srli_si128 (result, 4), 0); + result = _mm_xor_si128 (result, w); + w = _mm_clmulepi64_si128 (prim_poly, _mm_srli_si128 (result, 4), 0); + result = _mm_xor_si128 (result, w); + w = _mm_clmulepi64_si128 (prim_poly, _mm_srli_si128 (result, 4), 0); + result = _mm_xor_si128 (result, w); + d32[i] ^= ((gf_val_32_t)_mm_extract_epi32(result, 0)); + } + } else { + for (i = 0; i < bytes/sizeof(uint32_t); i++) { + b = _mm_insert_epi32 (a, s32[i], 0); + result = _mm_clmulepi64_si128 (a, b, 0); + w = _mm_clmulepi64_si128 (prim_poly, _mm_srli_si128 (result, 4), 0); + result = _mm_xor_si128 (result, w); + w = _mm_clmulepi64_si128 (prim_poly, _mm_srli_si128 (result, 4), 0); + result = _mm_xor_si128 (result, w); + w = _mm_clmulepi64_si128 (prim_poly, _mm_srli_si128 (result, 4), 0); + result = _mm_xor_si128 (result, w); + d32[i] = ((gf_val_32_t)_mm_extract_epi32(result, 0)); + } + } +} +#endif + +#if defined(INTEL_SSE4_PCLMUL) +static +void +gf_w32_clm_multiply_region_from_single_4(gf_t *gf, void *src, void *dest, uint32_t val, int bytes, int xor) +{ + uint32_t i; + uint32_t *s32; + uint32_t *d32; + + __m128i a, b; + __m128i result; + __m128i prim_poly; + __m128i w; + gf_internal_t * h = gf->scratch; + + prim_poly = _mm_set_epi32(0, 0, 1, (uint32_t)(h->prim_poly & 0xffffffffULL)); + + if (val == 0) { gf_multby_zero(dest, bytes, xor); return; } + if (val == 1) { gf_multby_one(src, dest, bytes, xor); return; } + + a = _mm_insert_epi32 (_mm_setzero_si128(), val, 0); + + s32 = (uint32_t *) src; + d32 = (uint32_t *) dest; + + if (xor) { + for (i = 0; i < bytes/sizeof(uint32_t); i++) { + b = _mm_insert_epi32 (a, s32[i], 0); + result = _mm_clmulepi64_si128 (a, b, 0); + w = _mm_clmulepi64_si128 (prim_poly, _mm_srli_si128 (result, 4), 0); + result = _mm_xor_si128 (result, w); + w = _mm_clmulepi64_si128 (prim_poly, _mm_srli_si128 (result, 4), 0); + result = _mm_xor_si128 (result, w); + w = _mm_clmulepi64_si128 (prim_poly, _mm_srli_si128 (result, 4), 0); + result = _mm_xor_si128 (result, w); + w = _mm_clmulepi64_si128 (prim_poly, _mm_srli_si128 (result, 4), 0); + result = _mm_xor_si128 (result, w); + d32[i] ^= ((gf_val_32_t)_mm_extract_epi32(result, 0)); + } + } else { + for (i = 0; i < bytes/sizeof(uint32_t); i++) { + b = _mm_insert_epi32 (a, s32[i], 0); + result = _mm_clmulepi64_si128 (a, b, 0); + w = _mm_clmulepi64_si128 (prim_poly, _mm_srli_si128 (result, 4), 0); + result = _mm_xor_si128 (result, w); + w = _mm_clmulepi64_si128 (prim_poly, _mm_srli_si128 (result, 4), 0); + result = _mm_xor_si128 (result, w); + w = _mm_clmulepi64_si128 (prim_poly, _mm_srli_si128 (result, 4), 0); + result = _mm_xor_si128 (result, w); + w = _mm_clmulepi64_si128 (prim_poly, _mm_srli_si128 (result, 4), 0); + result = _mm_xor_si128 (result, w); + d32[i] = ((gf_val_32_t)_mm_extract_epi32(result, 0)); + } + } +} +#endif + +static +inline +uint32_t gf_w32_euclid (gf_t *gf, uint32_t b) +{ + uint32_t e_i, e_im1, e_ip1; + uint32_t d_i, d_im1, d_ip1; + uint32_t y_i, y_im1, y_ip1; + uint32_t c_i; + + if (b == 0) return -1; + e_im1 = ((gf_internal_t *) (gf->scratch))->prim_poly; + e_i = b; + d_im1 = 32; + for (d_i = d_im1-1; ((1 << d_i) & e_i) == 0; d_i--) ; + y_i = 1; + y_im1 = 0; + + while (e_i != 1) { + + e_ip1 = e_im1; + d_ip1 = d_im1; + c_i = 0; + + while (d_ip1 >= d_i) { + c_i ^= (1 << (d_ip1 - d_i)); + e_ip1 ^= (e_i << (d_ip1 - d_i)); + d_ip1--; + if (e_ip1 == 0) return 0; + while ((e_ip1 & (1 << d_ip1)) == 0) d_ip1--; + } + + y_ip1 = y_im1 ^ gf->multiply.w32(gf, c_i, y_i); + y_im1 = y_i; + y_i = y_ip1; + + e_im1 = e_i; + d_im1 = d_i; + e_i = e_ip1; + d_i = d_ip1; + } + + return y_i; +} + +static +gf_val_32_t gf_w32_extract_word(gf_t *gf, void *start, int bytes, int index) +{ + uint32_t *r32, rv; + + r32 = (uint32_t *) start; + rv = r32[index]; + return rv; +} + +static +gf_val_32_t gf_w32_composite_extract_word(gf_t *gf, void *start, int bytes, int index) +{ + int sub_size; + gf_internal_t *h; + uint8_t *r8, *top; + uint32_t a, b, *r32; + gf_region_data rd; + + h = (gf_internal_t *) gf->scratch; + gf_set_region_data(&rd, gf, start, start, bytes, 0, 0, 32); + r32 = (uint32_t *) start; + if (r32 + index < (uint32_t *) rd.d_start) return r32[index]; + if (r32 + index >= (uint32_t *) rd.d_top) return r32[index]; + index -= (((uint32_t *) rd.d_start) - r32); + r8 = (uint8_t *) rd.d_start; + top = (uint8_t *) rd.d_top; + sub_size = (top-r8)/2; + + a = h->base_gf->extract_word.w32(h->base_gf, r8, sub_size, index); + b = h->base_gf->extract_word.w32(h->base_gf, r8+sub_size, sub_size, index); + return (a | (b << 16)); +} + +static +gf_val_32_t gf_w32_split_extract_word(gf_t *gf, void *start, int bytes, int index) +{ + int i; + uint32_t *r32, rv; + uint8_t *r8; + gf_region_data rd; + + gf_set_region_data(&rd, gf, start, start, bytes, 0, 0, 64); + r32 = (uint32_t *) start; + if (r32 + index < (uint32_t *) rd.d_start) return r32[index]; + if (r32 + index >= (uint32_t *) rd.d_top) return r32[index]; + index -= (((uint32_t *) rd.d_start) - r32); + r8 = (uint8_t *) rd.d_start; + r8 += ((index & 0xfffffff0)*4); + r8 += (index & 0xf); + r8 += 48; + rv =0; + for (i = 0; i < 4; i++) { + rv <<= 8; + rv |= *r8; + r8 -= 16; + } + return rv; +} + + +static +inline +uint32_t gf_w32_matrix (gf_t *gf, uint32_t b) +{ + return gf_bitmatrix_inverse(b, 32, ((gf_internal_t *) (gf->scratch))->prim_poly); +} + +/* JSP: GF_MULT_SHIFT: The world's dumbest multiplication algorithm. I only + include it for completeness. It does have the feature that it requires no + extra memory. +*/ + +#if defined(INTEL_SSE4_PCLMUL) + +static +inline +gf_val_32_t +gf_w32_cfmgk_multiply (gf_t *gf, gf_val_32_t a32, gf_val_32_t b32) +{ + gf_val_32_t rv = 0; + + __m128i a, b; + __m128i result; + __m128i w; + __m128i g, q; + gf_internal_t * h = gf->scratch; + uint64_t g_star, q_plus; + + q_plus = *(uint64_t *) h->private; + g_star = *((uint64_t *) h->private + 1); + + a = _mm_insert_epi32 (_mm_setzero_si128(), a32, 0); + b = _mm_insert_epi32 (a, b32, 0); + g = _mm_insert_epi64 (a, g_star, 0); + q = _mm_insert_epi64 (a, q_plus, 0); + + result = _mm_clmulepi64_si128 (a, b, 0); + w = _mm_clmulepi64_si128 (q, _mm_srli_si128 (result, 4), 0); + w = _mm_clmulepi64_si128 (g, _mm_srli_si128 (w, 4), 0); + result = _mm_xor_si128 (result, w); + + /* Extracts 32 bit value from result. */ + rv = ((gf_val_32_t)_mm_extract_epi32(result, 0)); + return rv; +} +#endif + +#if defined(INTEL_SSE4_PCLMUL) + +static +void +gf_w32_cfmgk_multiply_region_from_single(gf_t *gf, void *src, void *dest, uint32_t val, int bytes, int xor) +{ + + uint32_t i; + uint32_t *s32; + uint32_t *d32; + + __m128i a, b; + __m128i result; + __m128i w; + __m128i g, q; + gf_internal_t * h = gf->scratch; + uint64_t g_star, q_plus; + + if (val == 0) { gf_multby_zero(dest, bytes, xor); return; } + if (val == 1) { gf_multby_one(src, dest, bytes, xor); return; } + + q_plus = *(uint64_t *) h->private; + g_star = *((uint64_t *) h->private + 1); + + a = _mm_insert_epi32 (_mm_setzero_si128(), val, 0); + g = _mm_insert_epi64 (a, g_star, 0); + q = _mm_insert_epi64 (a, q_plus, 0); + s32 = (uint32_t *) src; + d32 = (uint32_t *) dest; + + if (xor) { + for (i = 0; i < bytes/sizeof(uint32_t); i++) { + b = _mm_insert_epi32 (a, s32[i], 0); + result = _mm_clmulepi64_si128 (a, b, 0); + w = _mm_clmulepi64_si128 (q, _mm_srli_si128 (result, 4), 0); + w = _mm_clmulepi64_si128 (g, _mm_srli_si128 (w, 4), 0); + result = _mm_xor_si128 (result, w); + d32[i] ^= ((gf_val_32_t)_mm_extract_epi32(result, 0)); + } + } else { + for (i = 0; i < bytes/sizeof(uint32_t); i++) { + b = _mm_insert_epi32 (a, s32[i], 0); + result = _mm_clmulepi64_si128 (a, b, 0); + w = _mm_clmulepi64_si128 (q, _mm_srli_si128 (result, 4), 0); + w = _mm_clmulepi64_si128 (g, _mm_srli_si128 (w, 4), 0); + result = _mm_xor_si128 (result, w); + d32[i] = ((gf_val_32_t)_mm_extract_epi32(result, 0)); + } + } +} +#endif + + +#if defined(INTEL_SSE4_PCLMUL) + +static +inline +gf_val_32_t +gf_w32_clm_multiply_2 (gf_t *gf, gf_val_32_t a32, gf_val_32_t b32) +{ + gf_val_32_t rv = 0; + + __m128i a, b; + __m128i result; + __m128i prim_poly; + __m128i w; + gf_internal_t * h = gf->scratch; + + + a = _mm_insert_epi32 (_mm_setzero_si128(), a32, 0); + b = _mm_insert_epi32 (a, b32, 0); + + prim_poly = _mm_set_epi32(0, 0, 1, (uint32_t)(h->prim_poly & 0xffffffffULL)); + + /* Do the initial multiply */ + + result = _mm_clmulepi64_si128 (a, b, 0); + + /* Ben: Do prim_poly reduction twice. We are guaranteed that we will only + have to do the reduction at most twice, because (w-2)/z == 2. Where + z is equal to the number of zeros after the leading 1 + + _mm_clmulepi64_si128 is the carryless multiply operation. Here + _mm_srli_si128 shifts the result to the right by 4 bytes. This allows + us to multiply the prim_poly by the leading bits of the result. We + then xor the result of that operation back with the result.*/ + + w = _mm_clmulepi64_si128 (prim_poly, _mm_srli_si128 (result, 4), 0); + result = _mm_xor_si128 (result, w); + w = _mm_clmulepi64_si128 (prim_poly, _mm_srli_si128 (result, 4), 0); + result = _mm_xor_si128 (result, w); + + /* Extracts 32 bit value from result. */ + rv = ((gf_val_32_t)_mm_extract_epi32(result, 0)); + return rv; +} +#endif + +#if defined(INTEL_SSE4_PCLMUL) + +static +inline +gf_val_32_t +gf_w32_clm_multiply_3 (gf_t *gf, gf_val_32_t a32, gf_val_32_t b32) +{ + gf_val_32_t rv = 0; + + __m128i a, b; + __m128i result; + __m128i prim_poly; + __m128i w; + gf_internal_t * h = gf->scratch; + + + a = _mm_insert_epi32 (_mm_setzero_si128(), a32, 0); + b = _mm_insert_epi32 (a, b32, 0); + + prim_poly = _mm_set_epi32(0, 0, 1, (uint32_t)(h->prim_poly & 0xffffffffULL)); + + /* Do the initial multiply */ + + result = _mm_clmulepi64_si128 (a, b, 0); + + w = _mm_clmulepi64_si128 (prim_poly, _mm_srli_si128 (result, 4), 0); + result = _mm_xor_si128 (result, w); + w = _mm_clmulepi64_si128 (prim_poly, _mm_srli_si128 (result, 4), 0); + result = _mm_xor_si128 (result, w); + w = _mm_clmulepi64_si128 (prim_poly, _mm_srli_si128 (result, 4), 0); + result = _mm_xor_si128 (result, w); + + /* Extracts 32 bit value from result. */ + + rv = ((gf_val_32_t)_mm_extract_epi32(result, 0)); + return rv; +} +#endif + +#if defined(INTEL_SSE4_PCLMUL) + +static +inline +gf_val_32_t +gf_w32_clm_multiply_4 (gf_t *gf, gf_val_32_t a32, gf_val_32_t b32) +{ + gf_val_32_t rv = 0; + + __m128i a, b; + __m128i result; + __m128i prim_poly; + __m128i w; + gf_internal_t * h = gf->scratch; + + + a = _mm_insert_epi32 (_mm_setzero_si128(), a32, 0); + b = _mm_insert_epi32 (a, b32, 0); + + prim_poly = _mm_set_epi32(0, 0, 1, (uint32_t)(h->prim_poly & 0xffffffffULL)); + + /* Do the initial multiply */ + + result = _mm_clmulepi64_si128 (a, b, 0); + + w = _mm_clmulepi64_si128 (prim_poly, _mm_srli_si128 (result, 4), 0); + result = _mm_xor_si128 (result, w); + w = _mm_clmulepi64_si128 (prim_poly, _mm_srli_si128 (result, 4), 0); + result = _mm_xor_si128 (result, w); + w = _mm_clmulepi64_si128 (prim_poly, _mm_srli_si128 (result, 4), 0); + result = _mm_xor_si128 (result, w); + w = _mm_clmulepi64_si128 (prim_poly, _mm_srli_si128 (result, 4), 0); + result = _mm_xor_si128 (result, w); + + /* Extracts 32 bit value from result. */ + + rv = ((gf_val_32_t)_mm_extract_epi32(result, 0)); + return rv; +} +#endif + + +static +inline +uint32_t +gf_w32_shift_multiply (gf_t *gf, uint32_t a32, uint32_t b32) +{ + uint64_t product, i, pp, a, b, one; + gf_internal_t *h; + + a = a32; + b = b32; + h = (gf_internal_t *) gf->scratch; + one = 1; + pp = h->prim_poly | (one << 32); + + product = 0; + + for (i = 0; i < GF_FIELD_WIDTH; i++) { + if (a & (one << i)) product ^= (b << i); + } + for (i = (GF_FIELD_WIDTH*2-2); i >= GF_FIELD_WIDTH; i--) { + if (product & (one << i)) product ^= (pp << (i-GF_FIELD_WIDTH)); + } + return product; +} + + static +int gf_w32_cfmgk_init(gf_t *gf) +{ + SET_FUNCTION(gf,inverse,w32,gf_w32_euclid) + SET_FUNCTION(gf,multiply_region,w32,gf_w32_multiply_region_from_single) + +#if defined(INTEL_SSE4_PCLMUL) + if (gf_cpu_supports_intel_pclmul) { + gf_internal_t *h; + + h = (gf_internal_t *) gf->scratch; + SET_FUNCTION(gf,multiply,w32,gf_w32_cfmgk_multiply) + SET_FUNCTION(gf,multiply_region,w32,gf_w32_cfmgk_multiply_region_from_single) + + uint64_t *q_plus = (uint64_t *) h->private; + uint64_t *g_star = (uint64_t *) h->private + 1; + + uint64_t tmp = h->prim_poly << 32; + *q_plus = 1ULL << 32; + + int i; + for(i = 63; i >= 32; i--) + if((1ULL << i) & tmp) + { + *q_plus |= 1ULL << (i-32); + tmp ^= h->prim_poly << (i-32); + } + + *g_star = h->prim_poly & ((1ULL << 32) - 1); + + return 1; + } +#endif + + return 0; +} + + static +int gf_w32_cfm_init(gf_t *gf) +{ + SET_FUNCTION(gf,inverse,w32,gf_w32_euclid) + SET_FUNCTION(gf,multiply_region,w32,gf_w32_multiply_region_from_single) + + /*Ben: We also check to see if the prim poly will work for pclmul */ + /*Ben: Check to see how many reduction steps it will take*/ + +#if defined(INTEL_SSE4_PCLMUL) + if (gf_cpu_supports_intel_pclmul) { + gf_internal_t *h; + + h = (gf_internal_t *) gf->scratch; + + if ((0xfffe0000 & h->prim_poly) == 0){ + SET_FUNCTION(gf,multiply,w32,gf_w32_clm_multiply_2) + SET_FUNCTION(gf,multiply_region,w32,gf_w32_clm_multiply_region_from_single_2) + }else if ((0xffc00000 & h->prim_poly) == 0){ + SET_FUNCTION(gf,multiply,w32,gf_w32_clm_multiply_3) + SET_FUNCTION(gf,multiply_region,w32,gf_w32_clm_multiply_region_from_single_3) + }else if ((0xfe000000 & h->prim_poly) == 0){ + SET_FUNCTION(gf,multiply,w32,gf_w32_clm_multiply_4) + SET_FUNCTION(gf,multiply_region,w32,gf_w32_clm_multiply_region_from_single_4) + } else { + return 0; + } + return 1; + } + #endif + + return 0; +} + + static +int gf_w32_shift_init(gf_t *gf) +{ + SET_FUNCTION(gf,inverse,w32,gf_w32_euclid) + SET_FUNCTION(gf,multiply_region,w32,gf_w32_multiply_region_from_single) + SET_FUNCTION(gf,multiply,w32,gf_w32_shift_multiply) + return 1; +} + +static + void +gf_w32_group_set_shift_tables(uint32_t *shift, uint32_t val, gf_internal_t *h) +{ + uint32_t i; + uint32_t j; + + shift[0] = 0; + + for (i = 1; i < ((uint32_t)1 << h->arg1); i <<= 1) { + for (j = 0; j < i; j++) shift[i|j] = shift[j]^val; + if (val & GF_FIRST_BIT) { + val <<= 1; + val ^= h->prim_poly; + } else { + val <<= 1; + } + } +} + + static +void gf_w32_group_s_equals_r_multiply_region(gf_t *gf, void *src, void *dest, gf_val_32_t val, int bytes, int xor) +{ + int leftover, rs; + uint32_t p, l, ind, a32; + int bits_left; + int g_s; + gf_region_data rd; + uint32_t *s32, *d32, *top; + struct gf_w32_group_data *gd; + gf_internal_t *h = (gf_internal_t *) gf->scratch; + + if (val == 0) { gf_multby_zero(dest, bytes, xor); return; } + if (val == 1) { gf_multby_one(src, dest, bytes, xor); return; } + + gd = (struct gf_w32_group_data *) h->private; + g_s = h->arg1; + gf_w32_group_set_shift_tables(gd->shift, val, h); + + gf_set_region_data(&rd, gf, src, dest, bytes, val, xor, 4); + gf_do_initial_region_alignment(&rd); + + s32 = (uint32_t *) rd.s_start; + d32 = (uint32_t *) rd.d_start; + top = (uint32_t *) rd.d_top; + + leftover = 32 % g_s; + if (leftover == 0) leftover = g_s; + + while (d32 < top) { + rs = 32 - leftover; + a32 = *s32; + ind = a32 >> rs; + a32 <<= leftover; + p = gd->shift[ind]; + + bits_left = rs; + rs = 32 - g_s; + + while (bits_left > 0) { + bits_left -= g_s; + ind = a32 >> rs; + a32 <<= g_s; + l = p >> rs; + p = (gd->shift[ind] ^ gd->reduce[l] ^ (p << g_s)); + } + if (xor) p ^= *d32; + *d32 = p; + d32++; + s32++; + } + gf_do_final_region_alignment(&rd); +} + + static +void gf_w32_group_multiply_region(gf_t *gf, void *src, void *dest, gf_val_32_t val, int bytes, int xor) +{ + uint32_t *s32, *d32, *top; + int i; + int leftover; + uint64_t p, l, r; + uint32_t a32, ind; + int g_s, g_r; + struct gf_w32_group_data *gd; + gf_region_data rd; + + if (val == 0) { gf_multby_zero(dest, bytes, xor); return; } + if (val == 1) { gf_multby_one(src, dest, bytes, xor); return; } + + gf_internal_t *h = (gf_internal_t *) gf->scratch; + g_s = h->arg1; + g_r = h->arg2; + gd = (struct gf_w32_group_data *) h->private; + gf_w32_group_set_shift_tables(gd->shift, val, h); + + leftover = GF_FIELD_WIDTH % g_s; + if (leftover == 0) leftover = g_s; + + gd = (struct gf_w32_group_data *) h->private; + gf_w32_group_set_shift_tables(gd->shift, val, h); + + gf_set_region_data(&rd, gf, src, dest, bytes, val, xor, 4); + gf_do_initial_region_alignment(&rd); + + s32 = (uint32_t *) rd.s_start; + d32 = (uint32_t *) rd.d_start; + top = (uint32_t *) rd.d_top; + + while (d32 < top) { + a32 = *s32; + ind = a32 >> (GF_FIELD_WIDTH - leftover); + p = gd->shift[ind]; + p <<= g_s; + a32 <<= leftover; + + i = (GF_FIELD_WIDTH - leftover); + while (i > g_s) { + ind = a32 >> (GF_FIELD_WIDTH-g_s); + p ^= gd->shift[ind]; + a32 <<= g_s; + p <<= g_s; + i -= g_s; + } + + ind = a32 >> (GF_FIELD_WIDTH-g_s); + p ^= gd->shift[ind]; + + for (i = gd->tshift ; i >= 0; i -= g_r) { + l = p & (gd->rmask << i); + r = gd->reduce[l >> (i+32)]; + r <<= (i); + p ^= r; + } + + if (xor) p ^= *d32; + *d32 = p; + d32++; + s32++; + } + gf_do_final_region_alignment(&rd); +} + +static +inline +gf_val_32_t +gf_w32_group_s_equals_r_multiply(gf_t *gf, gf_val_32_t a, gf_val_32_t b) +{ + int leftover, rs; + uint32_t p, l, ind, a32; + int bits_left; + int g_s; + + struct gf_w32_group_data *gd; + gf_internal_t *h = (gf_internal_t *) gf->scratch; + g_s = h->arg1; + + gd = (struct gf_w32_group_data *) h->private; + gf_w32_group_set_shift_tables(gd->shift, b, h); + + leftover = 32 % g_s; + if (leftover == 0) leftover = g_s; + + rs = 32 - leftover; + a32 = a; + ind = a32 >> rs; + a32 <<= leftover; + p = gd->shift[ind]; + + bits_left = rs; + rs = 32 - g_s; + + while (bits_left > 0) { + bits_left -= g_s; + ind = a32 >> rs; + a32 <<= g_s; + l = p >> rs; + p = (gd->shift[ind] ^ gd->reduce[l] ^ (p << g_s)); + } + return p; +} + +static +inline +gf_val_32_t +gf_w32_group_4_4_multiply(gf_t *gf, gf_val_32_t a, gf_val_32_t b) +{ + uint32_t p, l, ind, a32; + + struct gf_w32_group_data *d44; + gf_internal_t *h = (gf_internal_t *) gf->scratch; + + d44 = (struct gf_w32_group_data *) h->private; + gf_w32_group_set_shift_tables(d44->shift, b, h); + + a32 = a; + ind = a32 >> 28; + a32 <<= 4; + p = d44->shift[ind]; + ind = a32 >> 28; + a32 <<= 4; + l = p >> 28; + p = (d44->shift[ind] ^ d44->reduce[l] ^ (p << 4)); + ind = a32 >> 28; + a32 <<= 4; + l = p >> 28; + p = (d44->shift[ind] ^ d44->reduce[l] ^ (p << 4)); + ind = a32 >> 28; + a32 <<= 4; + l = p >> 28; + p = (d44->shift[ind] ^ d44->reduce[l] ^ (p << 4)); + ind = a32 >> 28; + a32 <<= 4; + l = p >> 28; + p = (d44->shift[ind] ^ d44->reduce[l] ^ (p << 4)); + ind = a32 >> 28; + a32 <<= 4; + l = p >> 28; + p = (d44->shift[ind] ^ d44->reduce[l] ^ (p << 4)); + ind = a32 >> 28; + a32 <<= 4; + l = p >> 28; + p = (d44->shift[ind] ^ d44->reduce[l] ^ (p << 4)); + ind = a32 >> 28; + l = p >> 28; + p = (d44->shift[ind] ^ d44->reduce[l] ^ (p << 4)); + return p; +} + +static +inline +gf_val_32_t +gf_w32_group_multiply(gf_t *gf, gf_val_32_t a, gf_val_32_t b) +{ + int i; + int leftover; + uint64_t p, l, r; + uint32_t a32, ind; + int g_s, g_r; + struct gf_w32_group_data *gd; + + gf_internal_t *h = (gf_internal_t *) gf->scratch; + g_s = h->arg1; + g_r = h->arg2; + gd = (struct gf_w32_group_data *) h->private; + gf_w32_group_set_shift_tables(gd->shift, b, h); + + leftover = GF_FIELD_WIDTH % g_s; + if (leftover == 0) leftover = g_s; + + a32 = a; + ind = a32 >> (GF_FIELD_WIDTH - leftover); + p = gd->shift[ind]; + p <<= g_s; + a32 <<= leftover; + + i = (GF_FIELD_WIDTH - leftover); + while (i > g_s) { + ind = a32 >> (GF_FIELD_WIDTH-g_s); + p ^= gd->shift[ind]; + a32 <<= g_s; + p <<= g_s; + i -= g_s; + } + + ind = a32 >> (GF_FIELD_WIDTH-g_s); + p ^= gd->shift[ind]; + + for (i = gd->tshift ; i >= 0; i -= g_r) { + l = p & (gd->rmask << i); + r = gd->reduce[l >> (i+32)]; + r <<= (i); + p ^= r; + } + return p; +} + +static +inline +gf_val_32_t +gf_w32_bytwo_b_multiply (gf_t *gf, gf_val_32_t a, gf_val_32_t b) +{ + uint32_t prod, pp, bmask; + gf_internal_t *h; + + h = (gf_internal_t *) gf->scratch; + pp = h->prim_poly; + + prod = 0; + bmask = 0x80000000; + + while (1) { + if (a & 1) prod ^= b; + a >>= 1; + if (a == 0) return prod; + if (b & bmask) { + b = ((b << 1) ^ pp); + } else { + b <<= 1; + } + } +} + +static +inline +gf_val_32_t +gf_w32_bytwo_p_multiply (gf_t *gf, gf_val_32_t a, gf_val_32_t b) +{ + uint32_t prod, pp, pmask, amask; + gf_internal_t *h; + + h = (gf_internal_t *) gf->scratch; + pp = h->prim_poly; + + + prod = 0; + pmask = 0x80000000; + amask = 0x80000000; + + while (amask != 0) { + if (prod & pmask) { + prod = ((prod << 1) ^ pp); + } else { + prod <<= 1; + } + if (a & amask) prod ^= b; + amask >>= 1; + } + return prod; +} + +static +void +gf_w32_bytwo_p_nosse_multiply_region(gf_t *gf, void *src, void *dest, gf_val_32_t val, int bytes, int xor) +{ + uint64_t *s64, *d64, t1, t2, ta, prod, amask; + gf_region_data rd; + struct gf_w32_bytwo_data *btd; + + if (val == 0) { gf_multby_zero(dest, bytes, xor); return; } + if (val == 1) { gf_multby_one(src, dest, bytes, xor); return; } + + btd = (struct gf_w32_bytwo_data *) ((gf_internal_t *) (gf->scratch))->private; + + gf_set_region_data(&rd, gf, src, dest, bytes, val, xor, 8); + gf_do_initial_region_alignment(&rd); + + s64 = (uint64_t *) rd.s_start; + d64 = (uint64_t *) rd.d_start; + + if (xor) { + while (s64 < (uint64_t *) rd.s_top) { + prod = 0; + amask = 0x80000000; + ta = *s64; + while (amask != 0) { + AB2(btd->prim_poly, btd->mask1, btd->mask2, prod, t1, t2); + if (val & amask) prod ^= ta; + amask >>= 1; + } + *d64 ^= prod; + d64++; + s64++; + } + } else { + while (s64 < (uint64_t *) rd.s_top) { + prod = 0; + amask = 0x80000000; + ta = *s64; + while (amask != 0) { + AB2(btd->prim_poly, btd->mask1, btd->mask2, prod, t1, t2); + if (val & amask) prod ^= ta; + amask >>= 1; + } + *d64 = prod; + d64++; + s64++; + } + } + gf_do_final_region_alignment(&rd); +} + +#define BYTWO_P_ONESTEP {\ + SSE_AB2(pp, m1 ,m2, prod, t1, t2); \ + t1 = _mm_and_si128(v, one); \ + t1 = _mm_sub_epi32(t1, one); \ + t1 = _mm_and_si128(t1, ta); \ + prod = _mm_xor_si128(prod, t1); \ + v = _mm_srli_epi64(v, 1); } + +#ifdef INTEL_SSE2 +static +void +gf_w32_bytwo_p_sse_multiply_region(gf_t *gf, void *src, void *dest, gf_val_32_t val, int bytes, int xor) +{ + int i; + uint8_t *s8, *d8; + uint32_t vrev; + __m128i pp, m1, m2, ta, prod, t1, t2, tp, one, v; + struct gf_w32_bytwo_data *btd; + gf_region_data rd; + + if (val == 0) { gf_multby_zero(dest, bytes, xor); return; } + if (val == 1) { gf_multby_one(src, dest, bytes, xor); return; } + + btd = (struct gf_w32_bytwo_data *) ((gf_internal_t *) (gf->scratch))->private; + + gf_set_region_data(&rd, gf, src, dest, bytes, val, xor, 16); + gf_do_initial_region_alignment(&rd); + + vrev = 0; + for (i = 0; i < 32; i++) { + vrev <<= 1; + if (!(val & ((gf_val_32_t)1 << i))) vrev |= 1; + } + + s8 = (uint8_t *) rd.s_start; + d8 = (uint8_t *) rd.d_start; + + pp = _mm_set1_epi32(btd->prim_poly&0xffffffff); + m1 = _mm_set1_epi32((btd->mask1)&0xffffffff); + m2 = _mm_set1_epi32((btd->mask2)&0xffffffff); + one = _mm_set1_epi32(1); + + while (d8 < (uint8_t *) rd.d_top) { + prod = _mm_setzero_si128(); + v = _mm_set1_epi32(vrev); + ta = _mm_load_si128((__m128i *) s8); + tp = (!xor) ? _mm_setzero_si128() : _mm_load_si128((__m128i *) d8); + BYTWO_P_ONESTEP; BYTWO_P_ONESTEP; BYTWO_P_ONESTEP; BYTWO_P_ONESTEP; + BYTWO_P_ONESTEP; BYTWO_P_ONESTEP; BYTWO_P_ONESTEP; BYTWO_P_ONESTEP; + BYTWO_P_ONESTEP; BYTWO_P_ONESTEP; BYTWO_P_ONESTEP; BYTWO_P_ONESTEP; + BYTWO_P_ONESTEP; BYTWO_P_ONESTEP; BYTWO_P_ONESTEP; BYTWO_P_ONESTEP; + BYTWO_P_ONESTEP; BYTWO_P_ONESTEP; BYTWO_P_ONESTEP; BYTWO_P_ONESTEP; + BYTWO_P_ONESTEP; BYTWO_P_ONESTEP; BYTWO_P_ONESTEP; BYTWO_P_ONESTEP; + BYTWO_P_ONESTEP; BYTWO_P_ONESTEP; BYTWO_P_ONESTEP; BYTWO_P_ONESTEP; + BYTWO_P_ONESTEP; BYTWO_P_ONESTEP; BYTWO_P_ONESTEP; BYTWO_P_ONESTEP; + _mm_store_si128((__m128i *) d8, _mm_xor_si128(prod, tp)); + d8 += 16; + s8 += 16; + } + gf_do_final_region_alignment(&rd); +} +#endif + +static +void +gf_w32_bytwo_b_nosse_multiply_region(gf_t *gf, void *src, void *dest, gf_val_32_t val, int bytes, int xor) +{ + uint64_t *s64, *d64, t1, t2, ta, tb, prod; + struct gf_w32_bytwo_data *btd; + gf_region_data rd; + + if (val == 0) { gf_multby_zero(dest, bytes, xor); return; } + if (val == 1) { gf_multby_one(src, dest, bytes, xor); return; } + + gf_set_region_data(&rd, gf, src, dest, bytes, val, xor, 32); + gf_do_initial_region_alignment(&rd); + + btd = (struct gf_w32_bytwo_data *) ((gf_internal_t *) (gf->scratch))->private; + s64 = (uint64_t *) rd.s_start; + d64 = (uint64_t *) rd.d_start; + + switch (val) { + case 2: + if (xor) { + while (d64 < (uint64_t *) rd.d_top) { + ta = *s64; + AB2(btd->prim_poly, btd->mask1, btd->mask2, ta, t1, t2); + *d64 ^= ta; + d64++; + s64++; + } + } else { + while (d64 < (uint64_t *) rd.d_top) { + ta = *s64; + AB2(btd->prim_poly, btd->mask1, btd->mask2, ta, t1, t2); + *d64 = ta; + d64++; + s64++; + } + } + break; + case 3: + if (xor) { + while (d64 < (uint64_t *) rd.d_top) { + ta = *s64; + prod = ta; + AB2(btd->prim_poly, btd->mask1, btd->mask2, ta, t1, t2); + *d64 ^= (ta ^ prod); + d64++; + s64++; + } + } else { + while (d64 < (uint64_t *) rd.d_top) { + ta = *s64; + prod = ta; + AB2(btd->prim_poly, btd->mask1, btd->mask2, ta, t1, t2); + *d64 = (ta ^ prod); + d64++; + s64++; + } + } + break; + case 4: + if (xor) { + while (d64 < (uint64_t *) rd.d_top) { + ta = *s64; + AB2(btd->prim_poly, btd->mask1, btd->mask2, ta, t1, t2); + AB2(btd->prim_poly, btd->mask1, btd->mask2, ta, t1, t2); + *d64 ^= ta; + d64++; + s64++; + } + } else { + while (d64 < (uint64_t *) rd.d_top) { + ta = *s64; + AB2(btd->prim_poly, btd->mask1, btd->mask2, ta, t1, t2); + AB2(btd->prim_poly, btd->mask1, btd->mask2, ta, t1, t2); + *d64 = ta; + d64++; + s64++; + } + } + break; + case 5: + if (xor) { + while (d64 < (uint64_t *) rd.d_top) { + ta = *s64; + prod = ta; + AB2(btd->prim_poly, btd->mask1, btd->mask2, ta, t1, t2); + AB2(btd->prim_poly, btd->mask1, btd->mask2, ta, t1, t2); + *d64 ^= (ta ^ prod); + d64++; + s64++; + } + } else { + while (d64 < (uint64_t *) rd.d_top) { + ta = *s64; + prod = ta; + AB2(btd->prim_poly, btd->mask1, btd->mask2, ta, t1, t2); + AB2(btd->prim_poly, btd->mask1, btd->mask2, ta, t1, t2); + *d64 = ta ^ prod; + d64++; + s64++; + } + } + break; + default: + if (xor) { + while (d64 < (uint64_t *) rd.d_top) { + prod = *d64 ; + ta = *s64; + tb = val; + while (1) { + if (tb & 1) prod ^= ta; + tb >>= 1; + if (tb == 0) break; + AB2(btd->prim_poly, btd->mask1, btd->mask2, ta, t1, t2); + } + *d64 = prod; + d64++; + s64++; + } + } else { + while (d64 < (uint64_t *) rd.d_top) { + prod = 0 ; + ta = *s64; + tb = val; + while (1) { + if (tb & 1) prod ^= ta; + tb >>= 1; + if (tb == 0) break; + AB2(btd->prim_poly, btd->mask1, btd->mask2, ta, t1, t2); + } + *d64 = prod; + d64++; + s64++; + } + } + break; + } + gf_do_final_region_alignment(&rd); +} + +#ifdef INTEL_SSE2 +static +void +gf_w32_bytwo_b_sse_region_2_noxor(gf_region_data *rd, struct gf_w32_bytwo_data *btd) +{ + uint8_t *d8, *s8; + __m128i pp, m1, m2, t1, t2, va; + + s8 = (uint8_t *) rd->s_start; + d8 = (uint8_t *) rd->d_start; + + pp = _mm_set1_epi32(btd->prim_poly&0xffffffff); + m1 = _mm_set1_epi32((btd->mask1)&0xffffffff); + m2 = _mm_set1_epi32((btd->mask2)&0xffffffff); + + while (d8 < (uint8_t *) rd->d_top) { + va = _mm_load_si128 ((__m128i *)(s8)); + SSE_AB2(pp, m1, m2, va, t1, t2); + _mm_store_si128((__m128i *)d8, va); + d8 += 16; + s8 += 16; + } +} +#endif + +#ifdef INTEL_SSE2 +static +void +gf_w32_bytwo_b_sse_region_2_xor(gf_region_data *rd, struct gf_w32_bytwo_data *btd) +{ + uint8_t *d8, *s8; + __m128i pp, m1, m2, t1, t2, va, vb; + + s8 = (uint8_t *) rd->s_start; + d8 = (uint8_t *) rd->d_start; + + pp = _mm_set1_epi32(btd->prim_poly&0xffffffff); + m1 = _mm_set1_epi32((btd->mask1)&0xffffffff); + m2 = _mm_set1_epi32((btd->mask2)&0xffffffff); + + while (d8 < (uint8_t *) rd->d_top) { + va = _mm_load_si128 ((__m128i *)(s8)); + SSE_AB2(pp, m1, m2, va, t1, t2); + vb = _mm_load_si128 ((__m128i *)(d8)); + vb = _mm_xor_si128(vb, va); + _mm_store_si128((__m128i *)d8, vb); + d8 += 16; + s8 += 16; + } +} +#endif + + +#ifdef INTEL_SSE2 +static +void +gf_w32_bytwo_b_sse_multiply_region(gf_t *gf, void *src, void *dest, gf_val_32_t val, int bytes, int xor) +{ + uint32_t itb; + uint8_t *d8, *s8; + __m128i pp, m1, m2, t1, t2, va, vb; + struct gf_w32_bytwo_data *btd; + gf_region_data rd; + + if (val == 0) { gf_multby_zero(dest, bytes, xor); return; } + if (val == 1) { gf_multby_one(src, dest, bytes, xor); return; } + + gf_set_region_data(&rd, gf, src, dest, bytes, val, xor, 16); + gf_do_initial_region_alignment(&rd); + + btd = (struct gf_w32_bytwo_data *) ((gf_internal_t *) (gf->scratch))->private; + + if (val == 2) { + if (xor) { + gf_w32_bytwo_b_sse_region_2_xor(&rd, btd); + } else { + gf_w32_bytwo_b_sse_region_2_noxor(&rd, btd); + } + gf_do_final_region_alignment(&rd); + return; + } + + s8 = (uint8_t *) rd.s_start; + d8 = (uint8_t *) rd.d_start; + + pp = _mm_set1_epi32(btd->prim_poly&0xffffffff); + m1 = _mm_set1_epi32((btd->mask1)&0xffffffff); + m2 = _mm_set1_epi32((btd->mask2)&0xffffffff); + + while (d8 < (uint8_t *) rd.d_top) { + va = _mm_load_si128 ((__m128i *)(s8)); + vb = (!xor) ? _mm_setzero_si128() : _mm_load_si128 ((__m128i *)(d8)); + itb = val; + while (1) { + if (itb & 1) vb = _mm_xor_si128(vb, va); + itb >>= 1; + if (itb == 0) break; + SSE_AB2(pp, m1, m2, va, t1, t2); + } + _mm_store_si128((__m128i *)d8, vb); + d8 += 16; + s8 += 16; + } + + gf_do_final_region_alignment(&rd); +} +#endif + +static +int gf_w32_bytwo_init(gf_t *gf) +{ + gf_internal_t *h; + uint64_t ip, m1, m2; + struct gf_w32_bytwo_data *btd; + + h = (gf_internal_t *) gf->scratch; + btd = (struct gf_w32_bytwo_data *) (h->private); + ip = h->prim_poly & 0xffffffff; + m1 = 0xfffffffe; + m2 = 0x80000000; + btd->prim_poly = 0; + btd->mask1 = 0; + btd->mask2 = 0; + + while (ip != 0) { + btd->prim_poly |= ip; + btd->mask1 |= m1; + btd->mask2 |= m2; + ip <<= GF_FIELD_WIDTH; + m1 <<= GF_FIELD_WIDTH; + m2 <<= GF_FIELD_WIDTH; + } + + if (h->mult_type == GF_MULT_BYTWO_p) { + SET_FUNCTION(gf,multiply,w32,gf_w32_bytwo_p_multiply) + #ifdef INTEL_SSE2 + if (gf_cpu_supports_intel_sse2 && !(h->region_type & GF_REGION_NOSIMD)) { + SET_FUNCTION(gf,multiply_region,w32,gf_w32_bytwo_p_sse_multiply_region) + } else { + #endif + SET_FUNCTION(gf,multiply_region,w32,gf_w32_bytwo_p_nosse_multiply_region) + if(h->region_type & GF_REGION_SIMD) + return 0; + #ifdef INTEL_SSE2 + } + #endif + } else { + SET_FUNCTION(gf,multiply,w32,gf_w32_bytwo_b_multiply) + #ifdef INTEL_SSE2 + if (gf_cpu_supports_intel_sse2 && !(h->region_type & GF_REGION_NOSIMD)) { + SET_FUNCTION(gf,multiply_region,w32,gf_w32_bytwo_b_sse_multiply_region) + } else { + #endif + SET_FUNCTION(gf,multiply_region,w32,gf_w32_bytwo_b_nosse_multiply_region) + if(h->region_type & GF_REGION_SIMD) + return 0; + #ifdef INTEL_SSE2 + } + #endif + } + + SET_FUNCTION(gf,inverse,w32,gf_w32_euclid) + return 1; +} + +static +inline +uint32_t +gf_w32_split_8_8_multiply (gf_t *gf, uint32_t a32, uint32_t b32) +{ + uint32_t product, i, j, mask, tb; + gf_internal_t *h; + struct gf_w32_split_8_8_data *d8; + + h = (gf_internal_t *) gf->scratch; + d8 = (struct gf_w32_split_8_8_data *) h->private; + product = 0; + mask = 0xff; + + for (i = 0; i < 4; i++) { + tb = b32; + for (j = 0; j < 4; j++) { + product ^= d8->tables[i+j][a32&mask][tb&mask]; + tb >>= 8; + } + a32 >>= 8; + } + return product; +} + +static +inline +void +gf_w32_split_8_32_lazy_multiply_region(gf_t *gf, void *src, void *dest, uint32_t val, int bytes, int xor) +{ + gf_internal_t *h; + uint32_t *s32, *d32, *top, p, a, v; + struct gf_split_8_32_lazy_data *d8; + struct gf_w32_split_8_8_data *d88; + uint32_t *t[4]; + int i, j, k, change; + uint32_t pp; + gf_region_data rd; + + if (val == 0) { gf_multby_zero(dest, bytes, xor); return; } + if (val == 1) { gf_multby_one(src, dest, bytes, xor); return; } + + h = (gf_internal_t *) gf->scratch; + if (h->arg1 == 32 || h->arg2 == 32 || h->mult_type == GF_MULT_DEFAULT) { + d8 = (struct gf_split_8_32_lazy_data *) h->private; + for (i = 0; i < 4; i++) t[i] = d8->tables[i]; + change = (val != d8->last_value); + if (change) d8->last_value = val; + } else { + d88 = (struct gf_w32_split_8_8_data *) h->private; + for (i = 0; i < 4; i++) t[i] = d88->region_tables[i]; + change = (val != d88->last_value); + if (change) d88->last_value = val; + } + pp = h->prim_poly; + + gf_set_region_data(&rd, gf, src, dest, bytes, val, xor, 4); + gf_do_initial_region_alignment(&rd); + + s32 = (uint32_t *) rd.s_start; + d32 = (uint32_t *) rd.d_start; + top = (uint32_t *) rd.d_top; + + if (change) { + v = val; + for (i = 0; i < 4; i++) { + t[i][0] = 0; + for (j = 1; j < 256; j <<= 1) { + for (k = 0; k < j; k++) { + t[i][k^j] = (v ^ t[i][k]); + } + v = (v & GF_FIRST_BIT) ? ((v << 1) ^ pp) : (v << 1); + } + } + } + + while (d32 < top) { + p = (xor) ? *d32 : 0; + a = *s32; + i = 0; + while (a != 0) { + v = (a & 0xff); + p ^= t[i][v]; + a >>= 8; + i++; + } + *d32 = p; + d32++; + s32++; + } + gf_do_final_region_alignment(&rd); +} + +static +inline +void +gf_w32_split_16_32_lazy_multiply_region(gf_t *gf, void *src, void *dest, uint32_t val, int bytes, int xor) +{ + gf_internal_t *h; + uint32_t *s32, *d32, *top, p, a, v; + struct gf_split_16_32_lazy_data *d16; + uint32_t *t[2]; + int i, j, k, change; + uint32_t pp; + gf_region_data rd; + + if (val == 0) { gf_multby_zero(dest, bytes, xor); return; } + if (val == 1) { gf_multby_one(src, dest, bytes, xor); return; } + + h = (gf_internal_t *) gf->scratch; + d16 = (struct gf_split_16_32_lazy_data *) h->private; + for (i = 0; i < 2; i++) t[i] = d16->tables[i]; + change = (val != d16->last_value); + if (change) d16->last_value = val; + + pp = h->prim_poly; + + gf_set_region_data(&rd, gf, src, dest, bytes, val, xor, 4); + gf_do_initial_region_alignment(&rd); + + s32 = (uint32_t *) rd.s_start; + d32 = (uint32_t *) rd.d_start; + top = (uint32_t *) rd.d_top; + + if (change) { + v = val; + for (i = 0; i < 2; i++) { + t[i][0] = 0; + for (j = 1; j < (1 << 16); j <<= 1) { + for (k = 0; k < j; k++) { + t[i][k^j] = (v ^ t[i][k]); + } + v = (v & GF_FIRST_BIT) ? ((v << 1) ^ pp) : (v << 1); + } + } + } + + while (d32 < top) { + p = (xor) ? *d32 : 0; + a = *s32; + i = 0; + while (a != 0 && i < 2) { + v = (a & 0xffff); + p ^= t[i][v]; + a >>= 16; + i++; + } + *d32 = p; + d32++; + s32++; + } + gf_do_final_region_alignment(&rd); +} + +static +void +gf_w32_split_2_32_lazy_multiply_region(gf_t *gf, void *src, void *dest, uint32_t val, int bytes, int xor) +{ + gf_internal_t *h; + struct gf_split_2_32_lazy_data *ld; + int i; + uint32_t pp, v, v2, s, *s32, *d32, *top; + gf_region_data rd; + + if (val == 0) { gf_multby_zero(dest, bytes, xor); return; } + if (val == 1) { gf_multby_one(src, dest, bytes, xor); return; } + + gf_set_region_data(&rd, gf, src, dest, bytes, val, xor, 4); + gf_do_initial_region_alignment(&rd); + + h = (gf_internal_t *) gf->scratch; + pp = h->prim_poly; + + ld = (struct gf_split_2_32_lazy_data *) h->private; + + if (ld->last_value != val) { + v = val; + for (i = 0; i < 16; i++) { + v2 = (v << 1); + if (v & GF_FIRST_BIT) v2 ^= pp; + ld->tables[i][0] = 0; + ld->tables[i][1] = v; + ld->tables[i][2] = v2; + ld->tables[i][3] = (v2 ^ v); + v = (v2 << 1); + if (v2 & GF_FIRST_BIT) v ^= pp; + } + } + ld->last_value = val; + + s32 = (uint32_t *) rd.s_start; + d32 = (uint32_t *) rd.d_start; + top = (uint32_t *) rd.d_top; + + while (d32 != top) { + v = (xor) ? *d32 : 0; + s = *s32; + i = 0; + while (s != 0) { + v ^= ld->tables[i][s&3]; + s >>= 2; + i++; + } + *d32 = v; + d32++; + s32++; + } + gf_do_final_region_alignment(&rd); +} + +#ifdef INTEL_SSSE3 +static +void +gf_w32_split_2_32_lazy_sse_multiply_region(gf_t *gf, void *src, void *dest, uint32_t val, int bytes, int xor) +{ + gf_internal_t *h; + int i, tindex; + uint32_t pp, v, v2, *s32, *d32, *top; + __m128i vi, si, pi, shuffler, tables[16], adder, xi, mask1, mask2; + gf_region_data rd; + + if (val == 0) { gf_multby_zero(dest, bytes, xor); return; } + if (val == 1) { gf_multby_one(src, dest, bytes, xor); return; } + + gf_set_region_data(&rd, gf, src, dest, bytes, val, xor, 32); + gf_do_initial_region_alignment(&rd); + + h = (gf_internal_t *) gf->scratch; + pp = h->prim_poly; + + s32 = (uint32_t *) rd.s_start; + d32 = (uint32_t *) rd.d_start; + top = (uint32_t *) rd.d_top; + + v = val; + for (i = 0; i < 16; i++) { + v2 = (v << 1); + if (v & GF_FIRST_BIT) v2 ^= pp; + tables[i] = _mm_set_epi32(v2 ^ v, v2, v, 0); + v = (v2 << 1); + if (v2 & GF_FIRST_BIT) v ^= pp; + } + + shuffler = _mm_set_epi8(0xc, 0xc, 0xc, 0xc, 8, 8, 8, 8, 4, 4, 4, 4, 0, 0, 0, 0); + adder = _mm_set_epi8(3, 2, 1, 0, 3, 2, 1, 0, 3, 2, 1, 0, 3, 2, 1, 0); + mask1 = _mm_set1_epi8(0x3); + mask2 = _mm_set1_epi8(0xc); + + while (d32 != top) { + pi = (xor) ? _mm_load_si128 ((__m128i *) d32) : _mm_setzero_si128(); + vi = _mm_load_si128((__m128i *) s32); + + tindex = 0; + for (i = 0; i < 4; i++) { + si = _mm_shuffle_epi8(vi, shuffler); + + xi = _mm_and_si128(si, mask1); + xi = _mm_slli_epi16(xi, 2); + xi = _mm_xor_si128(xi, adder); + pi = _mm_xor_si128(pi, _mm_shuffle_epi8(tables[tindex], xi)); + tindex++; + + xi = _mm_and_si128(si, mask2); + xi = _mm_xor_si128(xi, adder); + pi = _mm_xor_si128(pi, _mm_shuffle_epi8(tables[tindex], xi)); + si = _mm_srli_epi16(si, 2); + tindex++; + + xi = _mm_and_si128(si, mask2); + xi = _mm_xor_si128(xi, adder); + pi = _mm_xor_si128(pi, _mm_shuffle_epi8(tables[tindex], xi)); + si = _mm_srli_epi16(si, 2); + tindex++; + + xi = _mm_and_si128(si, mask2); + xi = _mm_xor_si128(xi, adder); + pi = _mm_xor_si128(pi, _mm_shuffle_epi8(tables[tindex], xi)); + tindex++; + + vi = _mm_srli_epi32(vi, 8); + } + _mm_store_si128((__m128i *) d32, pi); + d32 += 4; + s32 += 4; + } + + gf_do_final_region_alignment(&rd); + +} +#endif + +static +void +gf_w32_split_4_32_lazy_multiply_region(gf_t *gf, void *src, void *dest, uint32_t val, int bytes, int xor) +{ + gf_internal_t *h; + struct gf_split_4_32_lazy_data *ld; + int i, j, k; + uint32_t pp, v, s, *s32, *d32, *top; + gf_region_data rd; + + if (val == 0) { gf_multby_zero(dest, bytes, xor); return; } + if (val == 1) { gf_multby_one(src, dest, bytes, xor); return; } + + h = (gf_internal_t *) gf->scratch; + pp = h->prim_poly; + + ld = (struct gf_split_4_32_lazy_data *) h->private; + + gf_set_region_data(&rd, gf, src, dest, bytes, val, xor, 4); + gf_do_initial_region_alignment(&rd); + + if (ld->last_value != val) { + v = val; + for (i = 0; i < 8; i++) { + ld->tables[i][0] = 0; + for (j = 1; j < 16; j <<= 1) { + for (k = 0; k < j; k++) { + ld->tables[i][k^j] = (v ^ ld->tables[i][k]); + } + v = (v & GF_FIRST_BIT) ? ((v << 1) ^ pp) : (v << 1); + } + } + } + ld->last_value = val; + + s32 = (uint32_t *) rd.s_start; + d32 = (uint32_t *) rd.d_start; + top = (uint32_t *) rd.d_top; + + while (d32 != top) { + v = (xor) ? *d32 : 0; + s = *s32; + i = 0; + while (s != 0) { + v ^= ld->tables[i][s&0xf]; + s >>= 4; + i++; + } + *d32 = v; + d32++; + s32++; + } + gf_do_final_region_alignment(&rd); +} + +#ifdef INTEL_SSSE3 +static +void +gf_w32_split_4_32_lazy_sse_altmap_multiply_region(gf_t *gf, void *src, void *dest, uint32_t val, int bytes, int xor) +{ + gf_internal_t *h; + int i, j, k; + uint32_t pp, v, *s32, *d32, *top; + __m128i si, tables[8][4], p0, p1, p2, p3, mask1, v0, v1, v2, v3; + struct gf_split_4_32_lazy_data *ld; + uint8_t btable[16]; + gf_region_data rd; + + if (val == 0) { gf_multby_zero(dest, bytes, xor); return; } + if (val == 1) { gf_multby_one(src, dest, bytes, xor); return; } + + h = (gf_internal_t *) gf->scratch; + pp = h->prim_poly; + + gf_set_region_data(&rd, gf, src, dest, bytes, val, xor, 64); + gf_do_initial_region_alignment(&rd); + + s32 = (uint32_t *) rd.s_start; + d32 = (uint32_t *) rd.d_start; + top = (uint32_t *) rd.d_top; + + ld = (struct gf_split_4_32_lazy_data *) h->private; + + v = val; + for (i = 0; i < 8; i++) { + ld->tables[i][0] = 0; + for (j = 1; j < 16; j <<= 1) { + for (k = 0; k < j; k++) { + ld->tables[i][k^j] = (v ^ ld->tables[i][k]); + } + v = (v & GF_FIRST_BIT) ? ((v << 1) ^ pp) : (v << 1); + } + for (j = 0; j < 4; j++) { + for (k = 0; k < 16; k++) { + btable[k] = (uint8_t) ld->tables[i][k]; + ld->tables[i][k] >>= 8; + } + tables[i][j] = _mm_loadu_si128((__m128i *) btable); + } + } + + mask1 = _mm_set1_epi8(0xf); + + if (xor) { + while (d32 != top) { + p0 = _mm_load_si128 ((__m128i *) d32); + p1 = _mm_load_si128 ((__m128i *) (d32+4)); + p2 = _mm_load_si128 ((__m128i *) (d32+8)); + p3 = _mm_load_si128 ((__m128i *) (d32+12)); + + v0 = _mm_load_si128((__m128i *) s32); s32 += 4; + v1 = _mm_load_si128((__m128i *) s32); s32 += 4; + v2 = _mm_load_si128((__m128i *) s32); s32 += 4; + v3 = _mm_load_si128((__m128i *) s32); s32 += 4; + + si = _mm_and_si128(v0, mask1); + p0 = _mm_xor_si128(p0, _mm_shuffle_epi8(tables[0][0], si)); + p1 = _mm_xor_si128(p1, _mm_shuffle_epi8(tables[0][1], si)); + p2 = _mm_xor_si128(p2, _mm_shuffle_epi8(tables[0][2], si)); + p3 = _mm_xor_si128(p3, _mm_shuffle_epi8(tables[0][3], si)); + + v0 = _mm_srli_epi32(v0, 4); + si = _mm_and_si128(v0, mask1); + p0 = _mm_xor_si128(p0, _mm_shuffle_epi8(tables[1][0], si)); + p1 = _mm_xor_si128(p1, _mm_shuffle_epi8(tables[1][1], si)); + p2 = _mm_xor_si128(p2, _mm_shuffle_epi8(tables[1][2], si)); + p3 = _mm_xor_si128(p3, _mm_shuffle_epi8(tables[1][3], si)); + + si = _mm_and_si128(v1, mask1); + p0 = _mm_xor_si128(p0, _mm_shuffle_epi8(tables[2][0], si)); + p1 = _mm_xor_si128(p1, _mm_shuffle_epi8(tables[2][1], si)); + p2 = _mm_xor_si128(p2, _mm_shuffle_epi8(tables[2][2], si)); + p3 = _mm_xor_si128(p3, _mm_shuffle_epi8(tables[2][3], si)); + + v1 = _mm_srli_epi32(v1, 4); + si = _mm_and_si128(v1, mask1); + p0 = _mm_xor_si128(p0, _mm_shuffle_epi8(tables[3][0], si)); + p1 = _mm_xor_si128(p1, _mm_shuffle_epi8(tables[3][1], si)); + p2 = _mm_xor_si128(p2, _mm_shuffle_epi8(tables[3][2], si)); + p3 = _mm_xor_si128(p3, _mm_shuffle_epi8(tables[3][3], si)); + + si = _mm_and_si128(v2, mask1); + p0 = _mm_xor_si128(p0, _mm_shuffle_epi8(tables[4][0], si)); + p1 = _mm_xor_si128(p1, _mm_shuffle_epi8(tables[4][1], si)); + p2 = _mm_xor_si128(p2, _mm_shuffle_epi8(tables[4][2], si)); + p3 = _mm_xor_si128(p3, _mm_shuffle_epi8(tables[4][3], si)); + + v2 = _mm_srli_epi32(v2, 4); + si = _mm_and_si128(v2, mask1); + p0 = _mm_xor_si128(p0, _mm_shuffle_epi8(tables[5][0], si)); + p1 = _mm_xor_si128(p1, _mm_shuffle_epi8(tables[5][1], si)); + p2 = _mm_xor_si128(p2, _mm_shuffle_epi8(tables[5][2], si)); + p3 = _mm_xor_si128(p3, _mm_shuffle_epi8(tables[5][3], si)); + + si = _mm_and_si128(v3, mask1); + p0 = _mm_xor_si128(p0, _mm_shuffle_epi8(tables[6][0], si)); + p1 = _mm_xor_si128(p1, _mm_shuffle_epi8(tables[6][1], si)); + p2 = _mm_xor_si128(p2, _mm_shuffle_epi8(tables[6][2], si)); + p3 = _mm_xor_si128(p3, _mm_shuffle_epi8(tables[6][3], si)); + + v3 = _mm_srli_epi32(v3, 4); + si = _mm_and_si128(v3, mask1); + p0 = _mm_xor_si128(p0, _mm_shuffle_epi8(tables[7][0], si)); + p1 = _mm_xor_si128(p1, _mm_shuffle_epi8(tables[7][1], si)); + p2 = _mm_xor_si128(p2, _mm_shuffle_epi8(tables[7][2], si)); + p3 = _mm_xor_si128(p3, _mm_shuffle_epi8(tables[7][3], si)); + + _mm_store_si128((__m128i *) d32, p0); + _mm_store_si128((__m128i *) (d32+4), p1); + _mm_store_si128((__m128i *) (d32+8), p2); + _mm_store_si128((__m128i *) (d32+12), p3); + d32 += 16; + } + } else { + while (d32 != top) { + + v0 = _mm_load_si128((__m128i *) s32); s32 += 4; + v1 = _mm_load_si128((__m128i *) s32); s32 += 4; + v2 = _mm_load_si128((__m128i *) s32); s32 += 4; + v3 = _mm_load_si128((__m128i *) s32); s32 += 4; + + si = _mm_and_si128(v0, mask1); + p0 = _mm_shuffle_epi8(tables[0][0], si); + p1 = _mm_shuffle_epi8(tables[0][1], si); + p2 = _mm_shuffle_epi8(tables[0][2], si); + p3 = _mm_shuffle_epi8(tables[0][3], si); + + v0 = _mm_srli_epi32(v0, 4); + si = _mm_and_si128(v0, mask1); + p0 = _mm_xor_si128(p0, _mm_shuffle_epi8(tables[1][0], si)); + p1 = _mm_xor_si128(p1, _mm_shuffle_epi8(tables[1][1], si)); + p2 = _mm_xor_si128(p2, _mm_shuffle_epi8(tables[1][2], si)); + p3 = _mm_xor_si128(p3, _mm_shuffle_epi8(tables[1][3], si)); + + si = _mm_and_si128(v1, mask1); + p0 = _mm_xor_si128(p0, _mm_shuffle_epi8(tables[2][0], si)); + p1 = _mm_xor_si128(p1, _mm_shuffle_epi8(tables[2][1], si)); + p2 = _mm_xor_si128(p2, _mm_shuffle_epi8(tables[2][2], si)); + p3 = _mm_xor_si128(p3, _mm_shuffle_epi8(tables[2][3], si)); + + v1 = _mm_srli_epi32(v1, 4); + si = _mm_and_si128(v1, mask1); + p0 = _mm_xor_si128(p0, _mm_shuffle_epi8(tables[3][0], si)); + p1 = _mm_xor_si128(p1, _mm_shuffle_epi8(tables[3][1], si)); + p2 = _mm_xor_si128(p2, _mm_shuffle_epi8(tables[3][2], si)); + p3 = _mm_xor_si128(p3, _mm_shuffle_epi8(tables[3][3], si)); + + si = _mm_and_si128(v2, mask1); + p0 = _mm_xor_si128(p0, _mm_shuffle_epi8(tables[4][0], si)); + p1 = _mm_xor_si128(p1, _mm_shuffle_epi8(tables[4][1], si)); + p2 = _mm_xor_si128(p2, _mm_shuffle_epi8(tables[4][2], si)); + p3 = _mm_xor_si128(p3, _mm_shuffle_epi8(tables[4][3], si)); + + v2 = _mm_srli_epi32(v2, 4); + si = _mm_and_si128(v2, mask1); + p0 = _mm_xor_si128(p0, _mm_shuffle_epi8(tables[5][0], si)); + p1 = _mm_xor_si128(p1, _mm_shuffle_epi8(tables[5][1], si)); + p2 = _mm_xor_si128(p2, _mm_shuffle_epi8(tables[5][2], si)); + p3 = _mm_xor_si128(p3, _mm_shuffle_epi8(tables[5][3], si)); + + si = _mm_and_si128(v3, mask1); + p0 = _mm_xor_si128(p0, _mm_shuffle_epi8(tables[6][0], si)); + p1 = _mm_xor_si128(p1, _mm_shuffle_epi8(tables[6][1], si)); + p2 = _mm_xor_si128(p2, _mm_shuffle_epi8(tables[6][2], si)); + p3 = _mm_xor_si128(p3, _mm_shuffle_epi8(tables[6][3], si)); + + v3 = _mm_srli_epi32(v3, 4); + si = _mm_and_si128(v3, mask1); + p0 = _mm_xor_si128(p0, _mm_shuffle_epi8(tables[7][0], si)); + p1 = _mm_xor_si128(p1, _mm_shuffle_epi8(tables[7][1], si)); + p2 = _mm_xor_si128(p2, _mm_shuffle_epi8(tables[7][2], si)); + p3 = _mm_xor_si128(p3, _mm_shuffle_epi8(tables[7][3], si)); + + _mm_store_si128((__m128i *) d32, p0); + _mm_store_si128((__m128i *) (d32+4), p1); + _mm_store_si128((__m128i *) (d32+8), p2); + _mm_store_si128((__m128i *) (d32+12), p3); + d32 += 16; + } + } + + gf_do_final_region_alignment(&rd); +} +#endif + + +#ifdef INTEL_SSSE3 +static +void +gf_w32_split_4_32_lazy_sse_multiply_region(gf_t *gf, void *src, void *dest, uint32_t val, int bytes, int xor) +{ + gf_internal_t *h; + int i, j, k; + uint32_t pp, v, *s32, *d32, *top, tmp_table[16]; + __m128i si, tables[8][4], p0, p1, p2, p3, mask1, v0, v1, v2, v3, mask8; + __m128i tv1, tv2, tv3, tv0; + uint8_t btable[16]; + gf_region_data rd; + + if (val == 0) { gf_multby_zero(dest, bytes, xor); return; } + if (val == 1) { gf_multby_one(src, dest, bytes, xor); return; } + + h = (gf_internal_t *) gf->scratch; + pp = h->prim_poly; + + gf_set_region_data(&rd, gf, src, dest, bytes, val, xor, 64); + gf_do_initial_region_alignment(&rd); + + s32 = (uint32_t *) rd.s_start; + d32 = (uint32_t *) rd.d_start; + top = (uint32_t *) rd.d_top; + + v = val; + for (i = 0; i < 8; i++) { + tmp_table[0] = 0; + for (j = 1; j < 16; j <<= 1) { + for (k = 0; k < j; k++) { + tmp_table[k^j] = (v ^ tmp_table[k]); + } + v = (v & GF_FIRST_BIT) ? ((v << 1) ^ pp) : (v << 1); + } + for (j = 0; j < 4; j++) { + for (k = 0; k < 16; k++) { + btable[k] = (uint8_t) tmp_table[k]; + tmp_table[k] >>= 8; + } + tables[i][j] = _mm_loadu_si128((__m128i *) btable); + } + } + + mask1 = _mm_set1_epi8(0xf); + mask8 = _mm_set1_epi16(0xff); + + if (xor) { + while (d32 != top) { + v0 = _mm_load_si128((__m128i *) s32); s32 += 4; + v1 = _mm_load_si128((__m128i *) s32); s32 += 4; + v2 = _mm_load_si128((__m128i *) s32); s32 += 4; + v3 = _mm_load_si128((__m128i *) s32); s32 += 4; + + p0 = _mm_srli_epi16(v0, 8); + p1 = _mm_srli_epi16(v1, 8); + p2 = _mm_srli_epi16(v2, 8); + p3 = _mm_srli_epi16(v3, 8); + + tv0 = _mm_and_si128(v0, mask8); + tv1 = _mm_and_si128(v1, mask8); + tv2 = _mm_and_si128(v2, mask8); + tv3 = _mm_and_si128(v3, mask8); + + v0 = _mm_packus_epi16(p1, p0); + v1 = _mm_packus_epi16(tv1, tv0); + v2 = _mm_packus_epi16(p3, p2); + v3 = _mm_packus_epi16(tv3, tv2); + + p0 = _mm_srli_epi16(v0, 8); + p1 = _mm_srli_epi16(v1, 8); + p2 = _mm_srli_epi16(v2, 8); + p3 = _mm_srli_epi16(v3, 8); + + tv0 = _mm_and_si128(v0, mask8); + tv1 = _mm_and_si128(v1, mask8); + tv2 = _mm_and_si128(v2, mask8); + tv3 = _mm_and_si128(v3, mask8); + + v0 = _mm_packus_epi16(p2, p0); + v1 = _mm_packus_epi16(p3, p1); + v2 = _mm_packus_epi16(tv2, tv0); + v3 = _mm_packus_epi16(tv3, tv1); + + si = _mm_and_si128(v0, mask1); + p0 = _mm_shuffle_epi8(tables[6][0], si); + p1 = _mm_shuffle_epi8(tables[6][1], si); + p2 = _mm_shuffle_epi8(tables[6][2], si); + p3 = _mm_shuffle_epi8(tables[6][3], si); + + v0 = _mm_srli_epi32(v0, 4); + si = _mm_and_si128(v0, mask1); + p0 = _mm_xor_si128(p0, _mm_shuffle_epi8(tables[7][0], si)); + p1 = _mm_xor_si128(p1, _mm_shuffle_epi8(tables[7][1], si)); + p2 = _mm_xor_si128(p2, _mm_shuffle_epi8(tables[7][2], si)); + p3 = _mm_xor_si128(p3, _mm_shuffle_epi8(tables[7][3], si)); + + si = _mm_and_si128(v1, mask1); + p0 = _mm_xor_si128(p0, _mm_shuffle_epi8(tables[4][0], si)); + p1 = _mm_xor_si128(p1, _mm_shuffle_epi8(tables[4][1], si)); + p2 = _mm_xor_si128(p2, _mm_shuffle_epi8(tables[4][2], si)); + p3 = _mm_xor_si128(p3, _mm_shuffle_epi8(tables[4][3], si)); + + v1 = _mm_srli_epi32(v1, 4); + si = _mm_and_si128(v1, mask1); + p0 = _mm_xor_si128(p0, _mm_shuffle_epi8(tables[5][0], si)); + p1 = _mm_xor_si128(p1, _mm_shuffle_epi8(tables[5][1], si)); + p2 = _mm_xor_si128(p2, _mm_shuffle_epi8(tables[5][2], si)); + p3 = _mm_xor_si128(p3, _mm_shuffle_epi8(tables[5][3], si)); + + si = _mm_and_si128(v2, mask1); + p0 = _mm_xor_si128(p0, _mm_shuffle_epi8(tables[2][0], si)); + p1 = _mm_xor_si128(p1, _mm_shuffle_epi8(tables[2][1], si)); + p2 = _mm_xor_si128(p2, _mm_shuffle_epi8(tables[2][2], si)); + p3 = _mm_xor_si128(p3, _mm_shuffle_epi8(tables[2][3], si)); + + v2 = _mm_srli_epi32(v2, 4); + si = _mm_and_si128(v2, mask1); + p0 = _mm_xor_si128(p0, _mm_shuffle_epi8(tables[3][0], si)); + p1 = _mm_xor_si128(p1, _mm_shuffle_epi8(tables[3][1], si)); + p2 = _mm_xor_si128(p2, _mm_shuffle_epi8(tables[3][2], si)); + p3 = _mm_xor_si128(p3, _mm_shuffle_epi8(tables[3][3], si)); + + si = _mm_and_si128(v3, mask1); + p0 = _mm_xor_si128(p0, _mm_shuffle_epi8(tables[0][0], si)); + p1 = _mm_xor_si128(p1, _mm_shuffle_epi8(tables[0][1], si)); + p2 = _mm_xor_si128(p2, _mm_shuffle_epi8(tables[0][2], si)); + p3 = _mm_xor_si128(p3, _mm_shuffle_epi8(tables[0][3], si)); + + v3 = _mm_srli_epi32(v3, 4); + si = _mm_and_si128(v3, mask1); + p0 = _mm_xor_si128(p0, _mm_shuffle_epi8(tables[1][0], si)); + p1 = _mm_xor_si128(p1, _mm_shuffle_epi8(tables[1][1], si)); + p2 = _mm_xor_si128(p2, _mm_shuffle_epi8(tables[1][2], si)); + p3 = _mm_xor_si128(p3, _mm_shuffle_epi8(tables[1][3], si)); + + tv0 = _mm_unpackhi_epi8(p1, p3); + tv1 = _mm_unpackhi_epi8(p0, p2); + tv2 = _mm_unpacklo_epi8(p1, p3); + tv3 = _mm_unpacklo_epi8(p0, p2); + + p0 = _mm_unpackhi_epi8(tv1, tv0); + p1 = _mm_unpacklo_epi8(tv1, tv0); + p2 = _mm_unpackhi_epi8(tv3, tv2); + p3 = _mm_unpacklo_epi8(tv3, tv2); + + v0 = _mm_load_si128 ((__m128i *) d32); + v1 = _mm_load_si128 ((__m128i *) (d32+4)); + v2 = _mm_load_si128 ((__m128i *) (d32+8)); + v3 = _mm_load_si128 ((__m128i *) (d32+12)); + + p0 = _mm_xor_si128(p0, v0); + p1 = _mm_xor_si128(p1, v1); + p2 = _mm_xor_si128(p2, v2); + p3 = _mm_xor_si128(p3, v3); + + _mm_store_si128((__m128i *) d32, p0); + _mm_store_si128((__m128i *) (d32+4), p1); + _mm_store_si128((__m128i *) (d32+8), p2); + _mm_store_si128((__m128i *) (d32+12), p3); + d32 += 16; + } + } else { + while (d32 != top) { + v0 = _mm_load_si128((__m128i *) s32); s32 += 4; + v1 = _mm_load_si128((__m128i *) s32); s32 += 4; + v2 = _mm_load_si128((__m128i *) s32); s32 += 4; + v3 = _mm_load_si128((__m128i *) s32); s32 += 4; + + p0 = _mm_srli_epi16(v0, 8); + p1 = _mm_srli_epi16(v1, 8); + p2 = _mm_srli_epi16(v2, 8); + p3 = _mm_srli_epi16(v3, 8); + + tv0 = _mm_and_si128(v0, mask8); + tv1 = _mm_and_si128(v1, mask8); + tv2 = _mm_and_si128(v2, mask8); + tv3 = _mm_and_si128(v3, mask8); + + v0 = _mm_packus_epi16(p1, p0); + v1 = _mm_packus_epi16(tv1, tv0); + v2 = _mm_packus_epi16(p3, p2); + v3 = _mm_packus_epi16(tv3, tv2); + + p0 = _mm_srli_epi16(v0, 8); + p1 = _mm_srli_epi16(v1, 8); + p2 = _mm_srli_epi16(v2, 8); + p3 = _mm_srli_epi16(v3, 8); + + tv0 = _mm_and_si128(v0, mask8); + tv1 = _mm_and_si128(v1, mask8); + tv2 = _mm_and_si128(v2, mask8); + tv3 = _mm_and_si128(v3, mask8); + + v0 = _mm_packus_epi16(p2, p0); + v1 = _mm_packus_epi16(p3, p1); + v2 = _mm_packus_epi16(tv2, tv0); + v3 = _mm_packus_epi16(tv3, tv1); + + si = _mm_and_si128(v0, mask1); + p0 = _mm_shuffle_epi8(tables[6][0], si); + p1 = _mm_shuffle_epi8(tables[6][1], si); + p2 = _mm_shuffle_epi8(tables[6][2], si); + p3 = _mm_shuffle_epi8(tables[6][3], si); + + v0 = _mm_srli_epi32(v0, 4); + si = _mm_and_si128(v0, mask1); + p0 = _mm_xor_si128(p0, _mm_shuffle_epi8(tables[7][0], si)); + p1 = _mm_xor_si128(p1, _mm_shuffle_epi8(tables[7][1], si)); + p2 = _mm_xor_si128(p2, _mm_shuffle_epi8(tables[7][2], si)); + p3 = _mm_xor_si128(p3, _mm_shuffle_epi8(tables[7][3], si)); + + si = _mm_and_si128(v1, mask1); + p0 = _mm_xor_si128(p0, _mm_shuffle_epi8(tables[4][0], si)); + p1 = _mm_xor_si128(p1, _mm_shuffle_epi8(tables[4][1], si)); + p2 = _mm_xor_si128(p2, _mm_shuffle_epi8(tables[4][2], si)); + p3 = _mm_xor_si128(p3, _mm_shuffle_epi8(tables[4][3], si)); + + v1 = _mm_srli_epi32(v1, 4); + si = _mm_and_si128(v1, mask1); + p0 = _mm_xor_si128(p0, _mm_shuffle_epi8(tables[5][0], si)); + p1 = _mm_xor_si128(p1, _mm_shuffle_epi8(tables[5][1], si)); + p2 = _mm_xor_si128(p2, _mm_shuffle_epi8(tables[5][2], si)); + p3 = _mm_xor_si128(p3, _mm_shuffle_epi8(tables[5][3], si)); + + si = _mm_and_si128(v2, mask1); + p0 = _mm_xor_si128(p0, _mm_shuffle_epi8(tables[2][0], si)); + p1 = _mm_xor_si128(p1, _mm_shuffle_epi8(tables[2][1], si)); + p2 = _mm_xor_si128(p2, _mm_shuffle_epi8(tables[2][2], si)); + p3 = _mm_xor_si128(p3, _mm_shuffle_epi8(tables[2][3], si)); + + v2 = _mm_srli_epi32(v2, 4); + si = _mm_and_si128(v2, mask1); + p0 = _mm_xor_si128(p0, _mm_shuffle_epi8(tables[3][0], si)); + p1 = _mm_xor_si128(p1, _mm_shuffle_epi8(tables[3][1], si)); + p2 = _mm_xor_si128(p2, _mm_shuffle_epi8(tables[3][2], si)); + p3 = _mm_xor_si128(p3, _mm_shuffle_epi8(tables[3][3], si)); + + si = _mm_and_si128(v3, mask1); + p0 = _mm_xor_si128(p0, _mm_shuffle_epi8(tables[0][0], si)); + p1 = _mm_xor_si128(p1, _mm_shuffle_epi8(tables[0][1], si)); + p2 = _mm_xor_si128(p2, _mm_shuffle_epi8(tables[0][2], si)); + p3 = _mm_xor_si128(p3, _mm_shuffle_epi8(tables[0][3], si)); + + v3 = _mm_srli_epi32(v3, 4); + si = _mm_and_si128(v3, mask1); + p0 = _mm_xor_si128(p0, _mm_shuffle_epi8(tables[1][0], si)); + p1 = _mm_xor_si128(p1, _mm_shuffle_epi8(tables[1][1], si)); + p2 = _mm_xor_si128(p2, _mm_shuffle_epi8(tables[1][2], si)); + p3 = _mm_xor_si128(p3, _mm_shuffle_epi8(tables[1][3], si)); + + tv0 = _mm_unpackhi_epi8(p1, p3); + tv1 = _mm_unpackhi_epi8(p0, p2); + tv2 = _mm_unpacklo_epi8(p1, p3); + tv3 = _mm_unpacklo_epi8(p0, p2); + + p0 = _mm_unpackhi_epi8(tv1, tv0); + p1 = _mm_unpacklo_epi8(tv1, tv0); + p2 = _mm_unpackhi_epi8(tv3, tv2); + p3 = _mm_unpacklo_epi8(tv3, tv2); + + _mm_store_si128((__m128i *) d32, p0); + _mm_store_si128((__m128i *) (d32+4), p1); + _mm_store_si128((__m128i *) (d32+8), p2); + _mm_store_si128((__m128i *) (d32+12), p3); + d32 += 16; + } + } + gf_do_final_region_alignment(&rd); +} +#endif + +static +int gf_w32_split_init(gf_t *gf) +{ + gf_internal_t *h; + struct gf_split_2_32_lazy_data *ld2; + struct gf_split_4_32_lazy_data *ld4; + struct gf_w32_split_8_8_data *d8; + struct gf_split_8_32_lazy_data *d32; + struct gf_split_16_32_lazy_data *d16; + uint32_t p, basep; + int i, j, exp; + + h = (gf_internal_t *) gf->scratch; + + /* Defaults */ + + SET_FUNCTION(gf,inverse,w32,gf_w32_euclid) + + /* JSP: First handle single multiplication: + If args == 8, then we're doing split 8 8. + Otherwise, if PCLMUL, we use that. + Otherwise, we use bytwo_p. + */ + + if (h->arg1 == 8 && h->arg2 == 8) { + SET_FUNCTION(gf,multiply,w32,gf_w32_split_8_8_multiply) +#if defined(INTEL_SSE4_PCLMUL) + } else if (gf_cpu_supports_intel_pclmul) { + if ((0xfffe0000 & h->prim_poly) == 0){ + SET_FUNCTION(gf,multiply,w32,gf_w32_clm_multiply_2) + } else if ((0xffc00000 & h->prim_poly) == 0){ + SET_FUNCTION(gf,multiply,w32,gf_w32_clm_multiply_3) + } else if ((0xfe000000 & h->prim_poly) == 0){ + SET_FUNCTION(gf,multiply,w32,gf_w32_clm_multiply_4) + } +#endif + } else { + SET_FUNCTION(gf,multiply,w32,gf_w32_bytwo_p_multiply) + } + + /* Easy cases: 16/32 and 2/32 */ + + if ((h->arg1 == 16 && h->arg2 == 32) || (h->arg1 == 32 && h->arg2 == 16)) { + d16 = (struct gf_split_16_32_lazy_data *) h->private; + d16->last_value = 0; + SET_FUNCTION(gf,multiply_region,w32,gf_w32_split_16_32_lazy_multiply_region) + return 1; + } + + if ((h->arg1 == 2 && h->arg2 == 32) || (h->arg1 == 32 && h->arg2 == 2)) { + ld2 = (struct gf_split_2_32_lazy_data *) h->private; + ld2->last_value = 0; + #ifdef INTEL_SSSE3 + if (gf_cpu_supports_intel_ssse3 && !(h->region_type & GF_REGION_NOSIMD)) { + SET_FUNCTION(gf,multiply_region,w32,gf_w32_split_2_32_lazy_sse_multiply_region) + } else { + #endif + SET_FUNCTION(gf,multiply_region,w32,gf_w32_split_2_32_lazy_multiply_region) + if(h->region_type & GF_REGION_SIMD) return 0; + #ifdef INTEL_SSSE3 + } + #endif + return 1; + } + + /* 4/32 or Default + SSE - There is no ALTMAP/NOSSE. */ + + + if ((h->arg1 == 4 && h->arg2 == 32) || (h->arg1 == 32 && h->arg2 == 4) || + ((gf_cpu_supports_intel_ssse3 || gf_cpu_supports_arm_neon) && h->mult_type == GF_REGION_DEFAULT)) { + ld4 = (struct gf_split_4_32_lazy_data *) h->private; + ld4->last_value = 0; + if ((h->region_type & GF_REGION_NOSIMD) || !(gf_cpu_supports_intel_ssse3 || gf_cpu_supports_arm_neon)) { + SET_FUNCTION(gf,multiply_region,w32,gf_w32_split_4_32_lazy_multiply_region) + } else if (gf_cpu_supports_arm_neon) { +#ifdef ARM_NEON + gf_w32_neon_split_init(gf); +#endif + } else if (h->region_type & GF_REGION_ALTMAP) { +#ifdef INTEL_SSSE3 + SET_FUNCTION(gf,multiply_region,w32,gf_w32_split_4_32_lazy_sse_altmap_multiply_region) +#endif + } else { +#ifdef INTEL_SSSE3 + SET_FUNCTION(gf,multiply_region,w32,gf_w32_split_4_32_lazy_sse_multiply_region) +#endif + } + return 1; + } + + /* 8/32 or Default + no SSE */ + + if ((h->arg1 == 8 && h->arg2 == 32) || (h->arg1 == 32 && h->arg2 == 8) || + h->mult_type == GF_MULT_DEFAULT) { + d32 = (struct gf_split_8_32_lazy_data *) h->private; + d32->last_value = 0; + SET_FUNCTION(gf,multiply_region,w32,gf_w32_split_8_32_lazy_multiply_region) + return 1; + } + + /* Finally, if args == 8, then we have to set up the tables here. */ + + if (h->arg1 == 8 && h->arg2 == 8) { + d8 = (struct gf_w32_split_8_8_data *) h->private; + d8->last_value = 0; + SET_FUNCTION(gf,multiply,w32,gf_w32_split_8_8_multiply) + SET_FUNCTION(gf,multiply_region,w32,gf_w32_split_8_32_lazy_multiply_region) + basep = 1; + for (exp = 0; exp < 7; exp++) { + for (j = 0; j < 256; j++) d8->tables[exp][0][j] = 0; + for (i = 0; i < 256; i++) d8->tables[exp][i][0] = 0; + d8->tables[exp][1][1] = basep; + for (i = 2; i < 256; i++) { + if (i&1) { + p = d8->tables[exp][i^1][1]; + d8->tables[exp][i][1] = p ^ basep; + } else { + p = d8->tables[exp][i>>1][1]; + d8->tables[exp][i][1] = GF_MULTBY_TWO(p); + } + } + for (i = 1; i < 256; i++) { + p = d8->tables[exp][i][1]; + for (j = 1; j < 256; j++) { + if (j&1) { + d8->tables[exp][i][j] = d8->tables[exp][i][j^1] ^ p; + } else { + d8->tables[exp][i][j] = GF_MULTBY_TWO(d8->tables[exp][i][j>>1]); + } + } + } + for (i = 0; i < 8; i++) basep = GF_MULTBY_TWO(basep); + } + return 1; + } + + /* If we get here, then the arguments were bad. */ + + return 0; +} + +static +int gf_w32_group_init(gf_t *gf) +{ + uint32_t i, j, p, index; + struct gf_w32_group_data *gd; + gf_internal_t *h = (gf_internal_t *) gf->scratch; + uint32_t g_r, g_s; + + g_s = h->arg1; + g_r = h->arg2; + + gd = (struct gf_w32_group_data *) h->private; + gd->shift = (uint32_t *) (&(gd->memory)); + gd->reduce = gd->shift + (1 << g_s); + + gd->rmask = (1 << g_r) - 1; + gd->rmask <<= 32; + + gd->tshift = 32 % g_s; + if (gd->tshift == 0) gd->tshift = g_s; + gd->tshift = (32 - gd->tshift); + gd->tshift = ((gd->tshift-1)/g_r) * g_r; + + gd->reduce[0] = 0; + for (i = 0; i < ((uint32_t)1 << g_r); i++) { + p = 0; + index = 0; + for (j = 0; j < g_r; j++) { + if (i & (1 << j)) { + p ^= (h->prim_poly << j); + index ^= (1 << j); + index ^= (h->prim_poly >> (32-j)); + } + } + gd->reduce[index] = p; + } + + if (g_s == g_r) { + SET_FUNCTION(gf,multiply,w32,gf_w32_group_s_equals_r_multiply) + SET_FUNCTION(gf,multiply_region,w32,gf_w32_group_s_equals_r_multiply_region) + } else { + SET_FUNCTION(gf,multiply,w32,gf_w32_group_multiply) + SET_FUNCTION(gf,multiply_region,w32,gf_w32_group_multiply_region) + } + SET_FUNCTION(gf,divide,w32,NULL) + SET_FUNCTION(gf,inverse,w32,gf_w32_euclid) + + return 1; +} + + +static +uint32_t +gf_w32_composite_multiply_recursive(gf_t *gf, uint32_t a, uint32_t b) +{ + gf_internal_t *h = (gf_internal_t *) gf->scratch; + gf_t *base_gf = h->base_gf; + uint32_t b0 = b & 0x0000ffff; + uint32_t b1 = (b & 0xffff0000) >> 16; + uint32_t a0 = a & 0x0000ffff; + uint32_t a1 = (a & 0xffff0000) >> 16; + uint32_t a1b1; + uint32_t rv; + a1b1 = base_gf->multiply.w32(base_gf, a1, b1); + + rv = ((base_gf->multiply.w32(base_gf, a1, b0) ^ base_gf->multiply.w32(base_gf, a0, b1) ^ base_gf->multiply.w32(base_gf, a1b1, h->prim_poly)) << 16) | (base_gf->multiply.w32(base_gf, a0, b0) ^ a1b1); + return rv; +} + +/* JSP: This could be made faster. Someday, when I'm bored. */ + +static +uint32_t +gf_w32_composite_multiply_inline(gf_t *gf, uint32_t a, uint32_t b) +{ + gf_internal_t *h = (gf_internal_t *) gf->scratch; + uint32_t b0 = b & 0x0000ffff; + uint32_t b1 = b >> 16; + uint32_t a0 = a & 0x0000ffff; + uint32_t a1 = a >> 16; + uint32_t a1b1, prod; + uint16_t *log, *alog; + struct gf_w32_composite_data *cd; + + cd = (struct gf_w32_composite_data *) h->private; + log = cd->log; + alog = cd->alog; + + a1b1 = GF_W16_INLINE_MULT(log, alog, a1, b1); + prod = GF_W16_INLINE_MULT(log, alog, a1, b0); + prod ^= GF_W16_INLINE_MULT(log, alog, a0, b1); + prod ^= GF_W16_INLINE_MULT(log, alog, a1b1, h->prim_poly); + prod <<= 16; + prod ^= GF_W16_INLINE_MULT(log, alog, a0, b0); + prod ^= a1b1; + return prod; +} + +/* + * Composite field division trick (explained in 2007 tech report) + * + * Compute a / b = a*b^-1, where p(x) = x^2 + sx + 1 + * + * let c = b^-1 + * + * c*b = (s*b1c1+b1c0+b0c1)x+(b1c1+b0c0) + * + * want (s*b1c1+b1c0+b0c1) = 0 and (b1c1+b0c0) = 1 + * + * let d = b1c1 and d+1 = b0c0 + * + * solve s*b1c1+b1c0+b0c1 = 0 + * + * solution: d = (b1b0^-1)(b1b0^-1+b0b1^-1+s)^-1 + * + * c0 = (d+1)b0^-1 + * c1 = d*b1^-1 + * + * a / b = a * c + */ + +static +uint32_t +gf_w32_composite_inverse(gf_t *gf, uint32_t a) +{ + gf_internal_t *h = (gf_internal_t *) gf->scratch; + gf_t *base_gf = h->base_gf; + uint16_t a0 = a & 0x0000ffff; + uint16_t a1 = (a & 0xffff0000) >> 16; + uint16_t c0, c1, d, tmp; + uint32_t c; + uint16_t a0inv, a1inv; + + if (a0 == 0) { + a1inv = base_gf->inverse.w32(base_gf, a1); + c0 = base_gf->multiply.w32(base_gf, a1inv, h->prim_poly); + c1 = a1inv; + } else if (a1 == 0) { + c0 = base_gf->inverse.w32(base_gf, a0); + c1 = 0; + } else { + a1inv = base_gf->inverse.w32(base_gf, a1); + a0inv = base_gf->inverse.w32(base_gf, a0); + + d = base_gf->multiply.w32(base_gf, a1, a0inv); + + tmp = (base_gf->multiply.w32(base_gf, a1, a0inv) ^ base_gf->multiply.w32(base_gf, a0, a1inv) ^ h->prim_poly); + tmp = base_gf->inverse.w32(base_gf, tmp); + + d = base_gf->multiply.w32(base_gf, d, tmp); + + c0 = base_gf->multiply.w32(base_gf, (d^1), a0inv); + c1 = base_gf->multiply.w32(base_gf, d, a1inv); + } + + c = c0 | (c1 << 16); + + return c; +} + +static +void +gf_w32_composite_multiply_region(gf_t *gf, void *src, void *dest, uint32_t val, int bytes, int xor) +{ + gf_internal_t *h = (gf_internal_t *) gf->scratch; + gf_t *base_gf = h->base_gf; + uint32_t b0 = val & 0x0000ffff; + uint32_t b1 = (val & 0xffff0000) >> 16; + uint32_t *s32, *d32, *top; + uint16_t a0, a1, a1b1, *log, *alog; + uint32_t prod; + gf_region_data rd; + struct gf_w32_composite_data *cd; + + cd = (struct gf_w32_composite_data *) h->private; + log = cd->log; + alog = cd->alog; + + if (val == 0) { gf_multby_zero(dest, bytes, xor); return; } + gf_set_region_data(&rd, gf, src, dest, bytes, val, xor, 4); + + s32 = rd.s_start; + d32 = rd.d_start; + top = rd.d_top; + + if (log == NULL) { + if (xor) { + while (d32 < top) { + a0 = *s32 & 0x0000ffff; + a1 = (*s32 & 0xffff0000) >> 16; + a1b1 = base_gf->multiply.w32(base_gf, a1, b1); + + *d32 ^= ((base_gf->multiply.w32(base_gf, a0, b0) ^ a1b1) | + ((base_gf->multiply.w32(base_gf, a1, b0) ^ base_gf->multiply.w32(base_gf, a0, b1) ^ base_gf->multiply.w32(base_gf, a1b1, h->prim_poly)) << 16)); + s32++; + d32++; + } + } else { + while (d32 < top) { + a0 = *s32 & 0x0000ffff; + a1 = (*s32 & 0xffff0000) >> 16; + a1b1 = base_gf->multiply.w32(base_gf, a1, b1); + + *d32 = ((base_gf->multiply.w32(base_gf, a0, b0) ^ a1b1) | + ((base_gf->multiply.w32(base_gf, a1, b0) ^ base_gf->multiply.w32(base_gf, a0, b1) ^ base_gf->multiply.w32(base_gf, a1b1, h->prim_poly)) << 16)); + s32++; + d32++; + } + } + } else { + if (xor) { + while (d32 < top) { + a0 = *s32 & 0x0000ffff; + a1 = (*s32 & 0xffff0000) >> 16; + a1b1 = GF_W16_INLINE_MULT(log, alog, a1, b1); + + prod = GF_W16_INLINE_MULT(log, alog, a1, b0); + prod ^= GF_W16_INLINE_MULT(log, alog, a0, b1); + prod ^= GF_W16_INLINE_MULT(log, alog, a1b1, h->prim_poly); + prod <<= 16; + prod ^= GF_W16_INLINE_MULT(log, alog, a0, b0); + prod ^= a1b1; + *d32 ^= prod; + s32++; + d32++; + } + } else { + while (d32 < top) { + a0 = *s32 & 0x0000ffff; + a1 = (*s32 & 0xffff0000) >> 16; + a1b1 = GF_W16_INLINE_MULT(log, alog, a1, b1); + + prod = GF_W16_INLINE_MULT(log, alog, a1, b0); + prod ^= GF_W16_INLINE_MULT(log, alog, a0, b1); + prod ^= GF_W16_INLINE_MULT(log, alog, a1b1, h->prim_poly); + prod <<= 16; + prod ^= GF_W16_INLINE_MULT(log, alog, a0, b0); + prod ^= a1b1; + + *d32 = prod; + s32++; + d32++; + } + } + } +} + +static +void +gf_w32_composite_multiply_region_alt(gf_t *gf, void *src, void *dest, uint32_t val, int bytes, int xor) +{ + gf_internal_t *h = (gf_internal_t *) gf->scratch; + gf_t *base_gf = h->base_gf; + uint16_t val0 = val & 0x0000ffff; + uint16_t val1 = (val & 0xffff0000) >> 16; + gf_region_data rd; + int sub_reg_size; + uint8_t *slow, *shigh; + uint8_t *dlow, *dhigh, *top; + + /* JSP: I want the two pointers aligned wrt each other on 16 byte + boundaries. So I'm going to make sure that the area on + which the two operate is a multiple of 32. Of course, that + junks up the mapping, but so be it -- that's why we have extract_word.... */ + + gf_set_region_data(&rd, gf, src, dest, bytes, val, xor, 32); + gf_do_initial_region_alignment(&rd); + + slow = (uint8_t *) rd.s_start; + dlow = (uint8_t *) rd.d_start; + top = (uint8_t *) rd.d_top; + sub_reg_size = (top - dlow)/2; + shigh = slow + sub_reg_size; + dhigh = dlow + sub_reg_size; + + base_gf->multiply_region.w32(base_gf, slow, dlow, val0, sub_reg_size, xor); + base_gf->multiply_region.w32(base_gf, shigh, dlow, val1, sub_reg_size, 1); + base_gf->multiply_region.w32(base_gf, slow, dhigh, val1, sub_reg_size, xor); + base_gf->multiply_region.w32(base_gf, shigh, dhigh, val0, sub_reg_size, 1); + base_gf->multiply_region.w32(base_gf, shigh, dhigh, base_gf->multiply.w32(base_gf, h->prim_poly, val1), sub_reg_size, 1); + + gf_do_final_region_alignment(&rd); +} + +static +int gf_w32_composite_init(gf_t *gf) +{ + gf_internal_t *h = (gf_internal_t *) gf->scratch; + struct gf_w32_composite_data *cd; + + if (h->base_gf == NULL) return 0; + + cd = (struct gf_w32_composite_data *) h->private; + cd->log = gf_w16_get_log_table(h->base_gf); + cd->alog = gf_w16_get_mult_alog_table(h->base_gf); + + if (h->region_type & GF_REGION_ALTMAP) { + SET_FUNCTION(gf,multiply_region,w32,gf_w32_composite_multiply_region_alt) + } else { + SET_FUNCTION(gf,multiply_region,w32,gf_w32_composite_multiply_region) + } + + if (cd->log == NULL) { + SET_FUNCTION(gf,multiply,w32,gf_w32_composite_multiply_recursive) + } else { + SET_FUNCTION(gf,multiply,w32,gf_w32_composite_multiply_inline) + } + SET_FUNCTION(gf,divide,w32,NULL) + SET_FUNCTION(gf,inverse,w32,gf_w32_composite_inverse) + + return 1; +} + + + +int gf_w32_scratch_size(int mult_type, int region_type, int divide_type, int arg1, int arg2) +{ + switch(mult_type) + { + case GF_MULT_BYTWO_p: + case GF_MULT_BYTWO_b: + return sizeof(gf_internal_t) + sizeof(struct gf_w32_bytwo_data) + 64; + break; + case GF_MULT_GROUP: + return sizeof(gf_internal_t) + sizeof(struct gf_w32_group_data) + + sizeof(uint32_t) * (1 << arg1) + + sizeof(uint32_t) * (1 << arg2) + 64; + break; + case GF_MULT_DEFAULT: + + case GF_MULT_SPLIT_TABLE: + if (arg1 == 8 && arg2 == 8){ + return sizeof(gf_internal_t) + sizeof(struct gf_w32_split_8_8_data) + 64; + } + if ((arg1 == 16 && arg2 == 32) || (arg2 == 16 && arg1 == 32)) { + return sizeof(gf_internal_t) + sizeof(struct gf_split_16_32_lazy_data) + 64; + } + if ((arg1 == 2 && arg2 == 32) || (arg2 == 2 && arg1 == 32)) { + return sizeof(gf_internal_t) + sizeof(struct gf_split_2_32_lazy_data) + 64; + } + if ((arg1 == 8 && arg2 == 32) || (arg2 == 8 && arg1 == 32) || + (mult_type == GF_MULT_DEFAULT && !(gf_cpu_supports_intel_ssse3 || gf_cpu_supports_arm_neon))) { + return sizeof(gf_internal_t) + sizeof(struct gf_split_8_32_lazy_data) + 64; + } + if ((arg1 == 4 && arg2 == 32) || + (arg2 == 4 && arg1 == 32) || + mult_type == GF_MULT_DEFAULT) { + return sizeof(gf_internal_t) + sizeof(struct gf_split_4_32_lazy_data) + 64; + } + return 0; + case GF_MULT_CARRY_FREE: + return sizeof(gf_internal_t); + break; + case GF_MULT_CARRY_FREE_GK: + return sizeof(gf_internal_t) + sizeof(uint64_t)*2; + break; + case GF_MULT_SHIFT: + return sizeof(gf_internal_t); + break; + case GF_MULT_COMPOSITE: + return sizeof(gf_internal_t) + sizeof(struct gf_w32_composite_data) + 64; + break; + + default: + return 0; + } + return 0; +} + +int gf_w32_init(gf_t *gf) +{ + gf_internal_t *h; + + h = (gf_internal_t *) gf->scratch; + + /* Allen: set default primitive polynomial / irreducible polynomial if needed */ + + if (h->prim_poly == 0) { + if (h->mult_type == GF_MULT_COMPOSITE) { + h->prim_poly = gf_composite_get_default_poly(h->base_gf); + if (h->prim_poly == 0) return 0; /* This shouldn't happen */ + } else { + + /* Allen: use the following primitive polynomial to make carryless multiply work more efficiently for GF(2^32).*/ + + /* h->prim_poly = 0xc5; */ + + /* Allen: The following is the traditional primitive polynomial for GF(2^32) */ + + h->prim_poly = 0x400007; + } + } + + /* No leading one */ + + if(h->mult_type != GF_MULT_COMPOSITE) h->prim_poly &= 0xffffffff; + + SET_FUNCTION(gf,multiply,w32,NULL) + SET_FUNCTION(gf,divide,w32,NULL) + SET_FUNCTION(gf,inverse,w32,NULL) + SET_FUNCTION(gf,multiply_region,w32,NULL) + + switch(h->mult_type) { + case GF_MULT_CARRY_FREE: if (gf_w32_cfm_init(gf) == 0) return 0; break; + case GF_MULT_CARRY_FREE_GK: if (gf_w32_cfmgk_init(gf) == 0) return 0; break; + case GF_MULT_SHIFT: if (gf_w32_shift_init(gf) == 0) return 0; break; + case GF_MULT_COMPOSITE: if (gf_w32_composite_init(gf) == 0) return 0; break; + case GF_MULT_DEFAULT: + case GF_MULT_SPLIT_TABLE: if (gf_w32_split_init(gf) == 0) return 0; break; + case GF_MULT_GROUP: if (gf_w32_group_init(gf) == 0) return 0; break; + case GF_MULT_BYTWO_p: + case GF_MULT_BYTWO_b: if (gf_w32_bytwo_init(gf) == 0) return 0; break; + default: return 0; + } + if (h->divide_type == GF_DIVIDE_EUCLID) { + SET_FUNCTION(gf,divide,w32,gf_w32_divide_from_inverse) + SET_FUNCTION(gf,inverse,w32,gf_w32_euclid) + } else if (h->divide_type == GF_DIVIDE_MATRIX) { + SET_FUNCTION(gf,divide,w32,gf_w32_divide_from_inverse) + SET_FUNCTION(gf,inverse,w32,gf_w32_matrix) + } + + if (gf->inverse.w32 != NULL && gf->divide.w32 == NULL) { + SET_FUNCTION(gf,divide,w32,gf_w32_divide_from_inverse) + } + if (gf->inverse.w32 == NULL && gf->divide.w32 != NULL) { + SET_FUNCTION(gf,inverse,w32,gf_w32_inverse_from_divide) + } + if (h->region_type == GF_REGION_CAUCHY) { + SET_FUNCTION(gf,extract_word,w32,gf_wgen_extract_word) + SET_FUNCTION(gf,multiply_region,w32,gf_wgen_cauchy_region) + } else if (h->region_type & GF_REGION_ALTMAP) { + if (h->mult_type == GF_MULT_COMPOSITE) { + SET_FUNCTION(gf,extract_word,w32,gf_w32_composite_extract_word) + } else { + SET_FUNCTION(gf,extract_word,w32,gf_w32_split_extract_word) + } + } else { + SET_FUNCTION(gf,extract_word,w32,gf_w32_extract_word) + } + return 1; +} diff --git a/IDA_new/gf-complete/src/gf_w4.c b/IDA_new/gf-complete/src/gf_w4.c new file mode 100644 index 0000000..3a7b953 --- /dev/null +++ b/IDA_new/gf-complete/src/gf_w4.c @@ -0,0 +1,2047 @@ +/* + * GF-Complete: A Comprehensive Open Source Library for Galois Field Arithmetic + * James S. Plank, Ethan L. Miller, Kevin M. Greenan, + * Benjamin A. Arnold, John A. Burnum, Adam W. Disney, Allen C. McBride. + * + * gf_w4.c + * + * Routines for 4-bit Galois fields + */ + +#include "gf_int.h" +#include +#include +#include "gf_w4.h" +#include "gf_cpu.h" + +#define AB2(ip, am1 ,am2, b, t1, t2) {\ + t1 = (b << 1) & am1;\ + t2 = b & am2; \ + t2 = ((t2 << 1) - (t2 >> (GF_FIELD_WIDTH-1))); \ + b = (t1 ^ (t2 & ip));} + +// ToDo(KMG/JSP): Why is 0x88 hard-coded? +#define SSE_AB2(pp, m1, va, t1, t2) {\ + t1 = _mm_and_si128(_mm_slli_epi64(va, 1), m1); \ + t2 = _mm_and_si128(va, _mm_set1_epi8(0x88)); \ + t2 = _mm_sub_epi64 (_mm_slli_epi64(t2, 1), _mm_srli_epi64(t2, (GF_FIELD_WIDTH-1))); \ + va = _mm_xor_si128(t1, _mm_and_si128(t2, pp)); } + +/* ------------------------------------------------------------ + JSP: These are basic and work from multiple implementations. + */ + +static +inline +gf_val_32_t gf_w4_inverse_from_divide (gf_t *gf, gf_val_32_t a) +{ + return gf->divide.w32(gf, 1, a); +} + +static +inline +gf_val_32_t gf_w4_divide_from_inverse (gf_t *gf, gf_val_32_t a, gf_val_32_t b) +{ + b = gf->inverse.w32(gf, b); + return gf->multiply.w32(gf, a, b); +} + +static +inline +gf_val_32_t gf_w4_euclid (gf_t *gf, gf_val_32_t b) +{ + gf_val_32_t e_i, e_im1, e_ip1; + gf_val_32_t d_i, d_im1, d_ip1; + gf_val_32_t y_i, y_im1, y_ip1; + gf_val_32_t c_i; + + if (b == 0) return -1; + e_im1 = ((gf_internal_t *) (gf->scratch))->prim_poly; + e_i = b; + d_im1 = 4; + for (d_i = d_im1; ((1 << d_i) & e_i) == 0; d_i--) ; + y_i = 1; + y_im1 = 0; + + while (e_i != 1) { + e_ip1 = e_im1; + d_ip1 = d_im1; + c_i = 0; + + while (d_ip1 >= d_i) { + c_i ^= (1 << (d_ip1 - d_i)); + e_ip1 ^= (e_i << (d_ip1 - d_i)); + if (e_ip1 == 0) return 0; + while ((e_ip1 & (1 << d_ip1)) == 0) d_ip1--; + } + + y_ip1 = y_im1 ^ gf->multiply.w32(gf, c_i, y_i); + y_im1 = y_i; + y_i = y_ip1; + + e_im1 = e_i; + d_im1 = d_i; + e_i = e_ip1; + d_i = d_ip1; + } + + return y_i; +} + +static +gf_val_32_t gf_w4_extract_word(gf_t *gf, void *start, int bytes, int index) +{ + uint8_t *r8, v; + + r8 = (uint8_t *) start; + v = r8[index/2]; + if (index%2) { + return v >> 4; + } else { + return v&0xf; + } +} + + +static +inline +gf_val_32_t gf_w4_matrix (gf_t *gf, gf_val_32_t b) +{ + return gf_bitmatrix_inverse(b, 4, ((gf_internal_t *) (gf->scratch))->prim_poly); +} + + +static +inline +gf_val_32_t +gf_w4_shift_multiply (gf_t *gf, gf_val_32_t a, gf_val_32_t b) +{ + uint8_t product, i, pp; + gf_internal_t *h; + + h = (gf_internal_t *) gf->scratch; + pp = h->prim_poly; + + product = 0; + + for (i = 0; i < GF_FIELD_WIDTH; i++) { + if (a & (1 << i)) product ^= (b << i); + } + for (i = (GF_FIELD_WIDTH*2-2); i >= GF_FIELD_WIDTH; i--) { + if (product & (1 << i)) product ^= (pp << (i-GF_FIELD_WIDTH)); + } + return product; +} + +/* Ben: This function works, but it is 33% slower than the normal shift mult */ + +#if defined(INTEL_SSE4_PCLMUL) +static +inline +gf_val_32_t +gf_w4_clm_multiply (gf_t *gf, gf_val_32_t a4, gf_val_32_t b4) +{ + gf_val_32_t rv = 0; + + __m128i a, b; + __m128i result; + __m128i prim_poly; + __m128i w; + gf_internal_t * h = gf->scratch; + + a = _mm_insert_epi32 (_mm_setzero_si128(), a4, 0); + b = _mm_insert_epi32 (a, b4, 0); + + prim_poly = _mm_set_epi32(0, 0, 0, (uint32_t)(h->prim_poly & 0x1fULL)); + + /* Do the initial multiply */ + + result = _mm_clmulepi64_si128 (a, b, 0); + + /* Ben/JSP: Do prim_poly reduction once. We are guaranteed that we will only + have to do the reduction only once, because (w-2)/z == 1. Where + z is equal to the number of zeros after the leading 1. + + _mm_clmulepi64_si128 is the carryless multiply operation. Here + _mm_srli_epi64 shifts the result to the right by 4 bits. This allows + us to multiply the prim_poly by the leading bits of the result. We + then xor the result of that operation back with the result. */ + + w = _mm_clmulepi64_si128 (prim_poly, _mm_srli_epi64 (result, 4), 0); + result = _mm_xor_si128 (result, w); + + /* Extracts 32 bit value from result. */ + + rv = ((gf_val_32_t)_mm_extract_epi32(result, 0)); + return rv; +} +#endif + +static +void +gf_w4_multiply_region_from_single(gf_t *gf, void *src, void *dest, gf_val_32_t val, int bytes, int + xor) +{ + gf_region_data rd; + uint8_t *s8; + uint8_t *d8; + + if (val == 0) { gf_multby_zero(dest, bytes, xor); return; } + if (val == 1) { gf_multby_one(src, dest, bytes, xor); return; } + + gf_set_region_data(&rd, gf, src, dest, bytes, val, xor, 1); + gf_do_initial_region_alignment(&rd); + + s8 = (uint8_t *) rd.s_start; + d8 = (uint8_t *) rd.d_start; + + if (xor) { + while (d8 < ((uint8_t *) rd.d_top)) { + *d8 ^= (gf->multiply.w32(gf, val, (*s8 & 0xf)) | + ((gf->multiply.w32(gf, val, (*s8 >> 4))) << 4)); + d8++; + s8++; + } + } else { + while (d8 < ((uint8_t *) rd.d_top)) { + *d8 = (gf->multiply.w32(gf, val, (*s8 & 0xf)) | + ((gf->multiply.w32(gf, val, (*s8 >> 4))) << 4)); + d8++; + s8++; + } + } + gf_do_final_region_alignment(&rd); +} + +/* ------------------------------------------------------------ + IMPLEMENTATION: LOG_TABLE: + + JSP: This is a basic log-antilog implementation. + I'm not going to spend any time optimizing it because the + other techniques are faster for both single and region + operations. + */ + +static +inline +gf_val_32_t +gf_w4_log_multiply (gf_t *gf, gf_val_32_t a, gf_val_32_t b) +{ + struct gf_logtable_data *ltd; + + ltd = (struct gf_logtable_data *) ((gf_internal_t *) (gf->scratch))->private; + return (a == 0 || b == 0) ? 0 : ltd->antilog_tbl[(unsigned)(ltd->log_tbl[a] + ltd->log_tbl[b])]; +} + +static +inline +gf_val_32_t +gf_w4_log_divide (gf_t *gf, gf_val_32_t a, gf_val_32_t b) +{ + int log_sum = 0; + struct gf_logtable_data *ltd; + + if (a == 0 || b == 0) return 0; + ltd = (struct gf_logtable_data *) ((gf_internal_t *) (gf->scratch))->private; + + log_sum = ltd->log_tbl[a] - ltd->log_tbl[b]; + return (ltd->antilog_tbl_div[log_sum]); +} + +static +void +gf_w4_log_multiply_region(gf_t *gf, void *src, void *dest, gf_val_32_t val, int bytes, int xor) +{ + int i; + uint8_t lv, b, c; + uint8_t *s8, *d8; + + struct gf_logtable_data *ltd; + + if (val == 0) { gf_multby_zero(dest, bytes, xor); return; } + if (val == 1) { gf_multby_one(src, dest, bytes, xor); return; } + + ltd = (struct gf_logtable_data *) ((gf_internal_t *) (gf->scratch))->private; + s8 = (uint8_t *) src; + d8 = (uint8_t *) dest; + + lv = ltd->log_tbl[val]; + + for (i = 0; i < bytes; i++) { + c = (xor) ? d8[i] : 0; + b = (s8[i] >> GF_FIELD_WIDTH); + c ^= (b == 0) ? 0 : (ltd->antilog_tbl[lv + ltd->log_tbl[b]] << GF_FIELD_WIDTH); + b = (s8[i] & 0xf); + c ^= (b == 0) ? 0 : ltd->antilog_tbl[lv + ltd->log_tbl[b]]; + d8[i] = c; + } +} + +static +int gf_w4_log_init(gf_t *gf) +{ + gf_internal_t *h; + struct gf_logtable_data *ltd; + int i, b; + + h = (gf_internal_t *) gf->scratch; + ltd = h->private; + + for (i = 0; i < GF_FIELD_SIZE; i++) + ltd->log_tbl[i]=0; + + ltd->antilog_tbl_div = ltd->antilog_tbl + (GF_FIELD_SIZE-1); + b = 1; + i = 0; + do { + if (ltd->log_tbl[b] != 0 && i != 0) { + fprintf(stderr, "Cannot construct log table: Polynomial is not primitive.\n\n"); + return 0; + } + ltd->log_tbl[b] = i; + ltd->antilog_tbl[i] = b; + ltd->antilog_tbl[i+GF_FIELD_SIZE-1] = b; + b <<= 1; + i++; + if (b & GF_FIELD_SIZE) b = b ^ h->prim_poly; + } while (b != 1); + + if (i != GF_FIELD_SIZE - 1) { + _gf_errno = GF_E_LOGPOLY; + return 0; + } + + SET_FUNCTION(gf,inverse,w32,gf_w4_inverse_from_divide) + SET_FUNCTION(gf,divide,w32,gf_w4_log_divide) + SET_FUNCTION(gf,multiply,w32,gf_w4_log_multiply) + SET_FUNCTION(gf,multiply_region,w32,gf_w4_log_multiply_region) + return 1; +} + +/* ------------------------------------------------------------ + IMPLEMENTATION: SINGLE TABLE: JSP. + */ + +static +inline +gf_val_32_t +gf_w4_single_table_multiply (gf_t *gf, gf_val_32_t a, gf_val_32_t b) +{ + struct gf_single_table_data *std; + + std = (struct gf_single_table_data *) ((gf_internal_t *) (gf->scratch))->private; + return std->mult[a][b]; +} + +static +inline +gf_val_32_t +gf_w4_single_table_divide (gf_t *gf, gf_val_32_t a, gf_val_32_t b) +{ + struct gf_single_table_data *std; + + std = (struct gf_single_table_data *) ((gf_internal_t *) (gf->scratch))->private; + return std->div[a][b]; +} + +static +void +gf_w4_single_table_multiply_region(gf_t *gf, void *src, void *dest, gf_val_32_t val, int bytes, int xor) +{ + int i; + uint8_t b, c; + uint8_t *s8, *d8; + + struct gf_single_table_data *std; + + if (val == 0) { gf_multby_zero(dest, bytes, xor); return; } + if (val == 1) { gf_multby_one(src, dest, bytes, xor); return; } + + std = (struct gf_single_table_data *) ((gf_internal_t *) (gf->scratch))->private; + s8 = (uint8_t *) src; + d8 = (uint8_t *) dest; + + for (i = 0; i < bytes; i++) { + c = (xor) ? d8[i] : 0; + b = (s8[i] >> GF_FIELD_WIDTH); + c ^= (std->mult[val][b] << GF_FIELD_WIDTH); + b = (s8[i] & 0xf); + c ^= (std->mult[val][b]); + d8[i] = c; + } +} + +#define MM_PRINT(s, r) { uint8_t blah[16]; printf("%-12s", s); _mm_storeu_si128((__m128i *)blah, r); for (i = 0; i < 16; i++) printf(" %02x", blah[i]); printf("\n"); } + +#ifdef INTEL_SSSE3 +static +void +gf_w4_single_table_sse_multiply_region(gf_t *gf, void *src, void *dest, gf_val_32_t val, int bytes, int xor) +{ + gf_region_data rd; + uint8_t *base, *sptr, *dptr, *top; + __m128i tl, loset, r, va, th; + + struct gf_single_table_data *std; + + if (val == 0) { gf_multby_zero(dest, bytes, xor); return; } + if (val == 1) { gf_multby_one(src, dest, bytes, xor); return; } + + gf_set_region_data(&rd, gf, src, dest, bytes, val, xor, 16); + + std = (struct gf_single_table_data *) ((gf_internal_t *) (gf->scratch))->private; + base = (uint8_t *) std->mult; + base += (val << GF_FIELD_WIDTH); + + gf_do_initial_region_alignment(&rd); + + tl = _mm_loadu_si128((__m128i *)base); + th = _mm_slli_epi64(tl, 4); + loset = _mm_set1_epi8 (0x0f); + + sptr = rd.s_start; + dptr = rd.d_start; + top = rd.s_top; + + while (sptr < (uint8_t *) top) { + va = _mm_load_si128 ((__m128i *)(sptr)); + r = _mm_and_si128 (loset, va); + r = _mm_shuffle_epi8 (tl, r); + va = _mm_srli_epi64 (va, 4); + va = _mm_and_si128 (loset, va); + va = _mm_shuffle_epi8 (th, va); + r = _mm_xor_si128 (r, va); + va = (xor) ? _mm_load_si128 ((__m128i *)(dptr)) : _mm_setzero_si128(); + r = _mm_xor_si128 (r, va); + _mm_store_si128 ((__m128i *)(dptr), r); + dptr += 16; + sptr += 16; + } + gf_do_final_region_alignment(&rd); + +} +#endif + +static +int gf_w4_single_table_init(gf_t *gf) +{ + gf_internal_t *h; + struct gf_single_table_data *std; + int a, b, prod; + + + h = (gf_internal_t *) gf->scratch; + std = (struct gf_single_table_data *)h->private; + + bzero(std->mult, sizeof(uint8_t) * GF_FIELD_SIZE * GF_FIELD_SIZE); + bzero(std->div, sizeof(uint8_t) * GF_FIELD_SIZE * GF_FIELD_SIZE); + + for (a = 1; a < GF_FIELD_SIZE; a++) { + for (b = 1; b < GF_FIELD_SIZE; b++) { + prod = gf_w4_shift_multiply(gf, a, b); + std->mult[a][b] = prod; + std->div[prod][b] = a; + } + } + + SET_FUNCTION(gf,inverse,w32,NULL) + SET_FUNCTION(gf,divide,w32,gf_w4_single_table_divide) + SET_FUNCTION(gf,multiply,w32,gf_w4_single_table_multiply) + #if defined(INTEL_SSSE3) + if (gf_cpu_supports_intel_ssse3 && !(h->region_type & (GF_REGION_NOSIMD | GF_REGION_CAUCHY))) { + SET_FUNCTION(gf,multiply_region,w32,gf_w4_single_table_sse_multiply_region) + } else { + #elif defined(ARM_NEON) + if (gf_cpu_supports_arm_neon && !(h->region_type & (GF_REGION_NOSIMD | GF_REGION_CAUCHY))) { + gf_w4_neon_single_table_init(gf); + } else { + #endif + SET_FUNCTION(gf,multiply_region,w32,gf_w4_single_table_multiply_region) + if (h->region_type & GF_REGION_SIMD) return 0; + #if defined(INTEL_SSSE3) || defined(ARM_NEON) + } + #endif + + return 1; +} + +/* ------------------------------------------------------------ + IMPLEMENTATION: DOUBLE TABLE: JSP. + */ + +static +inline +gf_val_32_t +gf_w4_double_table_multiply (gf_t *gf, gf_val_32_t a, gf_val_32_t b) +{ + struct gf_double_table_data *std; + + std = (struct gf_double_table_data *) ((gf_internal_t *) (gf->scratch))->private; + return std->mult[a][b]; +} + +static +inline +gf_val_32_t +gf_w4_double_table_divide (gf_t *gf, gf_val_32_t a, gf_val_32_t b) +{ + struct gf_double_table_data *std; + + std = (struct gf_double_table_data *) ((gf_internal_t *) (gf->scratch))->private; + return std->div[a][b]; +} + +static +void +gf_w4_double_table_multiply_region(gf_t *gf, void *src, void *dest, gf_val_32_t val, int bytes, int xor) +{ + int i; + uint8_t *s8, *d8, *base; + gf_region_data rd; + struct gf_double_table_data *std; + + if (val == 0) { gf_multby_zero(dest, bytes, xor); return; } + if (val == 1) { gf_multby_one(src, dest, bytes, xor); return; } + + gf_set_region_data(&rd, gf, src, dest, bytes, val, xor, 8); + + std = (struct gf_double_table_data *) ((gf_internal_t *) (gf->scratch))->private; + s8 = (uint8_t *) src; + d8 = (uint8_t *) dest; + base = (uint8_t *) std->mult; + base += (val << GF_DOUBLE_WIDTH); + + if (xor) { + for (i = 0; i < bytes; i++) d8[i] ^= base[s8[i]]; + } else { + for (i = 0; i < bytes; i++) d8[i] = base[s8[i]]; + } +} + +static +int gf_w4_double_table_init(gf_t *gf) +{ + gf_internal_t *h; + struct gf_double_table_data *std; + int a, b, c, prod, ab; + uint8_t mult[GF_FIELD_SIZE][GF_FIELD_SIZE]; + + h = (gf_internal_t *) gf->scratch; + std = (struct gf_double_table_data *)h->private; + + bzero(mult, sizeof(uint8_t) * GF_FIELD_SIZE * GF_FIELD_SIZE); + bzero(std->div, sizeof(uint8_t) * GF_FIELD_SIZE * GF_FIELD_SIZE); + + for (a = 1; a < GF_FIELD_SIZE; a++) { + for (b = 1; b < GF_FIELD_SIZE; b++) { + prod = gf_w4_shift_multiply(gf, a, b); + mult[a][b] = prod; + std->div[prod][b] = a; + } + } + bzero(std->mult, sizeof(uint8_t) * GF_FIELD_SIZE * GF_FIELD_SIZE * GF_FIELD_SIZE); + for (a = 0; a < GF_FIELD_SIZE; a++) { + for (b = 0; b < GF_FIELD_SIZE; b++) { + ab = mult[a][b]; + for (c = 0; c < GF_FIELD_SIZE; c++) { + std->mult[a][(b << 4) | c] = ((ab << 4) | mult[a][c]); + } + } + } + + SET_FUNCTION(gf,inverse,w32,NULL) + SET_FUNCTION(gf,divide,w32,gf_w4_double_table_divide) + SET_FUNCTION(gf,multiply,w32,gf_w4_double_table_multiply) + SET_FUNCTION(gf,multiply_region,w32,gf_w4_double_table_multiply_region) + return 1; +} + + +static +inline +gf_val_32_t +gf_w4_quad_table_lazy_divide (gf_t *gf, gf_val_32_t a, gf_val_32_t b) +{ + struct gf_quad_table_lazy_data *std; + + std = (struct gf_quad_table_lazy_data *) ((gf_internal_t *) (gf->scratch))->private; + return std->div[a][b]; +} + +static +inline +gf_val_32_t +gf_w4_quad_table_lazy_multiply (gf_t *gf, gf_val_32_t a, gf_val_32_t b) +{ + struct gf_quad_table_lazy_data *std; + + std = (struct gf_quad_table_lazy_data *) ((gf_internal_t *) (gf->scratch))->private; + return std->smult[a][b]; +} + +static +inline +gf_val_32_t +gf_w4_quad_table_divide (gf_t *gf, gf_val_32_t a, gf_val_32_t b) +{ + struct gf_quad_table_data *std; + + std = (struct gf_quad_table_data *) ((gf_internal_t *) (gf->scratch))->private; + return std->div[a][b]; +} + +static +inline +gf_val_32_t +gf_w4_quad_table_multiply (gf_t *gf, gf_val_32_t a, gf_val_32_t b) +{ + struct gf_quad_table_data *std; + uint16_t v; + + std = (struct gf_quad_table_data *) ((gf_internal_t *) (gf->scratch))->private; + v = std->mult[a][b]; + return v; +} + +static +void +gf_w4_quad_table_multiply_region(gf_t *gf, void *src, void *dest, gf_val_32_t val, int bytes, int xor) +{ + uint16_t *base; + gf_region_data rd; + struct gf_quad_table_data *std; + struct gf_quad_table_lazy_data *ltd; + gf_internal_t *h; + int a, b, c, d, va, vb, vc, vd; + + if (val == 0) { gf_multby_zero(dest, bytes, xor); return; } + if (val == 1) { gf_multby_one(src, dest, bytes, xor); return; } + + h = (gf_internal_t *) (gf->scratch); + if (h->region_type & GF_REGION_LAZY) { + ltd = (struct gf_quad_table_lazy_data *) ((gf_internal_t *) (gf->scratch))->private; + base = ltd->mult; + for (a = 0; a < 16; a++) { + va = (ltd->smult[val][a] << 12); + for (b = 0; b < 16; b++) { + vb = (ltd->smult[val][b] << 8); + for (c = 0; c < 16; c++) { + vc = (ltd->smult[val][c] << 4); + for (d = 0; d < 16; d++) { + vd = ltd->smult[val][d]; + base[(a << 12) | (b << 8) | (c << 4) | d ] = (va | vb | vc | vd); + } + } + } + } + } else { + std = (struct gf_quad_table_data *) ((gf_internal_t *) (gf->scratch))->private; + base = &(std->mult[val][0]); + } + + gf_set_region_data(&rd, gf, src, dest, bytes, val, xor, 8); + gf_do_initial_region_alignment(&rd); + gf_two_byte_region_table_multiply(&rd, base); + gf_do_final_region_alignment(&rd); +} + +static +int gf_w4_quad_table_init(gf_t *gf) +{ + gf_internal_t *h; + struct gf_quad_table_data *std; + int prod, val, a, b, c, d, va, vb, vc, vd; + uint8_t mult[GF_FIELD_SIZE][GF_FIELD_SIZE]; + + h = (gf_internal_t *) gf->scratch; + std = (struct gf_quad_table_data *)h->private; + + bzero(mult, sizeof(uint8_t) * GF_FIELD_SIZE * GF_FIELD_SIZE); + bzero(std->div, sizeof(uint8_t) * GF_FIELD_SIZE * GF_FIELD_SIZE); + + for (a = 1; a < GF_FIELD_SIZE; a++) { + for (b = 1; b < GF_FIELD_SIZE; b++) { + prod = gf_w4_shift_multiply(gf, a, b); + mult[a][b] = prod; + std->div[prod][b] = a; + } + } + + for (val = 0; val < 16; val++) { + for (a = 0; a < 16; a++) { + va = (mult[val][a] << 12); + for (b = 0; b < 16; b++) { + vb = (mult[val][b] << 8); + for (c = 0; c < 16; c++) { + vc = (mult[val][c] << 4); + for (d = 0; d < 16; d++) { + vd = mult[val][d]; + std->mult[val][(a << 12) | (b << 8) | (c << 4) | d ] = (va | vb | vc | vd); + } + } + } + } + } + + SET_FUNCTION(gf,inverse,w32,NULL) + SET_FUNCTION(gf,divide,w32,gf_w4_quad_table_divide) + SET_FUNCTION(gf,multiply,w32,gf_w4_quad_table_multiply) + SET_FUNCTION(gf,multiply_region,w32,gf_w4_quad_table_multiply_region) + return 1; +} +static +int gf_w4_quad_table_lazy_init(gf_t *gf) +{ + gf_internal_t *h; + struct gf_quad_table_lazy_data *std; + int a, b, prod, loga, logb; + uint8_t log_tbl[GF_FIELD_SIZE]; + uint8_t antilog_tbl[GF_FIELD_SIZE*2]; + + h = (gf_internal_t *) gf->scratch; + std = (struct gf_quad_table_lazy_data *)h->private; + + b = 1; + for (a = 0; a < GF_MULT_GROUP_SIZE; a++) { + log_tbl[b] = a; + antilog_tbl[a] = b; + antilog_tbl[a+GF_MULT_GROUP_SIZE] = b; + b <<= 1; + if (b & GF_FIELD_SIZE) { + b = b ^ h->prim_poly; + } + } + + bzero(std->smult, sizeof(uint8_t) * GF_FIELD_SIZE * GF_FIELD_SIZE); + bzero(std->div, sizeof(uint8_t) * GF_FIELD_SIZE * GF_FIELD_SIZE); + + for (a = 1; a < GF_FIELD_SIZE; a++) { + loga = log_tbl[a]; + for (b = 1; b < GF_FIELD_SIZE; b++) { + logb = log_tbl[b]; + prod = antilog_tbl[loga+logb]; + std->smult[a][b] = prod; + std->div[prod][b] = a; + } + } + + SET_FUNCTION(gf,inverse,w32,NULL) + SET_FUNCTION(gf,divide,w32,gf_w4_quad_table_lazy_divide) + SET_FUNCTION(gf,multiply,w32,gf_w4_quad_table_lazy_multiply) + SET_FUNCTION(gf,multiply_region,w32,gf_w4_quad_table_multiply_region) + return 1; +} + +static +int gf_w4_table_init(gf_t *gf) +{ + int rt; + gf_internal_t *h; + + h = (gf_internal_t *) gf->scratch; + rt = (h->region_type); + + if (h->mult_type == GF_MULT_DEFAULT && + !(gf_cpu_supports_intel_ssse3 || gf_cpu_supports_arm_neon)) + rt |= GF_REGION_DOUBLE_TABLE; + + if (rt & GF_REGION_DOUBLE_TABLE) { + return gf_w4_double_table_init(gf); + } else if (rt & GF_REGION_QUAD_TABLE) { + if (rt & GF_REGION_LAZY) { + return gf_w4_quad_table_lazy_init(gf); + } else { + return gf_w4_quad_table_init(gf); + } + } else { + return gf_w4_single_table_init(gf); + } + return 0; +} + +/* ------------------------------------------------------------ + JSP: GF_MULT_BYTWO_p and _b: See the paper. +*/ + +static +inline +gf_val_32_t +gf_w4_bytwo_p_multiply (gf_t *gf, gf_val_32_t a, gf_val_32_t b) +{ + uint32_t prod, pp, pmask, amask; + gf_internal_t *h; + + h = (gf_internal_t *) gf->scratch; + pp = h->prim_poly; + + + prod = 0; + pmask = 0x8; + amask = 0x8; + + while (amask != 0) { + if (prod & pmask) { + prod = ((prod << 1) ^ pp); + } else { + prod <<= 1; + } + if (a & amask) prod ^= b; + amask >>= 1; + } + return prod; +} + +static +inline +gf_val_32_t +gf_w4_bytwo_b_multiply (gf_t *gf, gf_val_32_t a, gf_val_32_t b) +{ + uint32_t prod, pp, bmask; + gf_internal_t *h; + + h = (gf_internal_t *) gf->scratch; + pp = h->prim_poly; + + prod = 0; + bmask = 0x8; + + while (1) { + if (a & 1) prod ^= b; + a >>= 1; + if (a == 0) return prod; + if (b & bmask) { + b = ((b << 1) ^ pp); + } else { + b <<= 1; + } + } +} + +static +void +gf_w4_bytwo_p_nosse_multiply_region(gf_t *gf, void *src, void *dest, gf_val_32_t val, int bytes, int xor) +{ + uint64_t *s64, *d64, t1, t2, ta, prod, amask; + gf_region_data rd; + struct gf_bytwo_data *btd; + + if (val == 0) { gf_multby_zero(dest, bytes, xor); return; } + if (val == 1) { gf_multby_one(src, dest, bytes, xor); return; } + + btd = (struct gf_bytwo_data *) ((gf_internal_t *) (gf->scratch))->private; + + gf_set_region_data(&rd, gf, src, dest, bytes, val, xor, 8); + gf_do_initial_region_alignment(&rd); + + s64 = (uint64_t *) rd.s_start; + d64 = (uint64_t *) rd.d_start; + + if (xor) { + while (s64 < (uint64_t *) rd.s_top) { + prod = 0; + amask = 0x8; + ta = *s64; + while (amask != 0) { + AB2(btd->prim_poly, btd->mask1, btd->mask2, prod, t1, t2); + if (val & amask) prod ^= ta; + amask >>= 1; + } + *d64 ^= prod; + d64++; + s64++; + } + } else { + while (s64 < (uint64_t *) rd.s_top) { + prod = 0; + amask = 0x8; + ta = *s64; + while (amask != 0) { + AB2(btd->prim_poly, btd->mask1, btd->mask2, prod, t1, t2); + if (val & amask) prod ^= ta; + amask >>= 1; + } + *d64 = prod; + d64++; + s64++; + } + } + gf_do_final_region_alignment(&rd); +} + +#define BYTWO_P_ONESTEP {\ + SSE_AB2(pp, m1, prod, t1, t2); \ + t1 = _mm_and_si128(v, one); \ + t1 = _mm_sub_epi8(t1, one); \ + t1 = _mm_and_si128(t1, ta); \ + prod = _mm_xor_si128(prod, t1); \ + v = _mm_srli_epi64(v, 1); } + +#ifdef INTEL_SSE2 +static +void +gf_w4_bytwo_p_sse_multiply_region(gf_t *gf, void *src, void *dest, gf_val_32_t val, int bytes, int xor) +{ + int i; + uint8_t *s8, *d8; + uint8_t vrev; + __m128i pp, m1, ta, prod, t1, t2, tp, one, v; + struct gf_bytwo_data *btd; + gf_region_data rd; + + if (val == 0) { gf_multby_zero(dest, bytes, xor); return; } + if (val == 1) { gf_multby_one(src, dest, bytes, xor); return; } + + btd = (struct gf_bytwo_data *) ((gf_internal_t *) (gf->scratch))->private; + + gf_set_region_data(&rd, gf, src, dest, bytes, val, xor, 16); + gf_do_initial_region_alignment(&rd); + + vrev = 0; + for (i = 0; i < 4; i++) { + vrev <<= 1; + if (!(val & (1 << i))) vrev |= 1; + } + + s8 = (uint8_t *) rd.s_start; + d8 = (uint8_t *) rd.d_start; + + pp = _mm_set1_epi8(btd->prim_poly&0xff); + m1 = _mm_set1_epi8((btd->mask1)&0xff); + one = _mm_set1_epi8(1); + + while (d8 < (uint8_t *) rd.d_top) { + prod = _mm_setzero_si128(); + v = _mm_set1_epi8(vrev); + ta = _mm_load_si128((__m128i *) s8); + tp = (!xor) ? _mm_setzero_si128() : _mm_load_si128((__m128i *) d8); + BYTWO_P_ONESTEP; + BYTWO_P_ONESTEP; + BYTWO_P_ONESTEP; + BYTWO_P_ONESTEP; + _mm_store_si128((__m128i *) d8, _mm_xor_si128(prod, tp)); + d8 += 16; + s8 += 16; + } + gf_do_final_region_alignment(&rd); +} +#endif + +/* +#ifdef INTEL_SSE2 +static +void +gf_w4_bytwo_b_sse_multiply_region(gf_t *gf, void *src, void *dest, gf_val_32_t val, int bytes, int xor) +{ + uint8_t *d8, *s8, tb; + __m128i pp, m1, m2, t1, t2, va, vb; + struct gf_bytwo_data *btd; + gf_region_data rd; + + if (val == 0) { gf_multby_zero(dest, bytes, xor); return; } + if (val == 1) { gf_multby_one(src, dest, bytes, xor); return; } + + gf_set_region_data(&rd, gf, src, dest, bytes, val, xor, 16); + gf_do_initial_region_alignment(&rd); + + s8 = (uint8_t *) rd.s_start; + d8 = (uint8_t *) rd.d_start; + + btd = (struct gf_bytwo_data *) ((gf_internal_t *) (gf->scratch))->private; + + pp = _mm_set1_epi8(btd->prim_poly&0xff); + m1 = _mm_set1_epi8((btd->mask1)&0xff); + m2 = _mm_set1_epi8((btd->mask2)&0xff); + + if (xor) { + while (d8 < (uint8_t *) rd.d_top) { + va = _mm_load_si128 ((__m128i *)(s8)); + vb = _mm_load_si128 ((__m128i *)(d8)); + tb = val; + while (1) { + if (tb & 1) vb = _mm_xor_si128(vb, va); + tb >>= 1; + if (tb == 0) break; + SSE_AB2(pp, m1, m2, va, t1, t2); + } + _mm_store_si128((__m128i *)d8, vb); + d8 += 16; + s8 += 16; + } + } else { + while (d8 < (uint8_t *) rd.d_top) { + va = _mm_load_si128 ((__m128i *)(s8)); + vb = _mm_setzero_si128 (); + tb = val; + while (1) { + if (tb & 1) vb = _mm_xor_si128(vb, va); + tb >>= 1; + if (tb == 0) break; + t1 = _mm_and_si128(_mm_slli_epi64(va, 1), m1); + t2 = _mm_and_si128(va, m2); + t2 = _mm_sub_epi64 ( + _mm_slli_epi64(t2, 1), _mm_srli_epi64(t2, (GF_FIELD_WIDTH-1))); + va = _mm_xor_si128(t1, _mm_and_si128(t2, pp)); + } + _mm_store_si128((__m128i *)d8, vb); + d8 += 16; + s8 += 16; + } + } + gf_do_final_region_alignment(&rd); +} +#endif +*/ + +#ifdef INTEL_SSE2 +static +void +gf_w4_bytwo_b_sse_region_2_noxor(gf_region_data *rd, struct gf_bytwo_data *btd) +{ + uint8_t *d8, *s8; + __m128i pp, m1, t1, t2, va; + + s8 = (uint8_t *) rd->s_start; + d8 = (uint8_t *) rd->d_start; + + pp = _mm_set1_epi8(btd->prim_poly&0xff); + m1 = _mm_set1_epi8((btd->mask1)&0xff); + + while (d8 < (uint8_t *) rd->d_top) { + va = _mm_load_si128 ((__m128i *)(s8)); + SSE_AB2(pp, m1, va, t1, t2); + _mm_store_si128((__m128i *)d8, va); + d8 += 16; + s8 += 16; + } +} +#endif + +#ifdef INTEL_SSE2 +static +void +gf_w4_bytwo_b_sse_region_2_xor(gf_region_data *rd, struct gf_bytwo_data *btd) +{ + uint8_t *d8, *s8; + __m128i pp, m1, t1, t2, va, vb; + + s8 = (uint8_t *) rd->s_start; + d8 = (uint8_t *) rd->d_start; + + pp = _mm_set1_epi8(btd->prim_poly&0xff); + m1 = _mm_set1_epi8((btd->mask1)&0xff); + + while (d8 < (uint8_t *) rd->d_top) { + va = _mm_load_si128 ((__m128i *)(s8)); + SSE_AB2(pp, m1, va, t1, t2); + vb = _mm_load_si128 ((__m128i *)(d8)); + vb = _mm_xor_si128(vb, va); + _mm_store_si128((__m128i *)d8, vb); + d8 += 16; + s8 += 16; + } +} +#endif + +#ifdef INTEL_SSE2 +static +void +gf_w4_bytwo_b_sse_region_4_noxor(gf_region_data *rd, struct gf_bytwo_data *btd) +{ + uint8_t *d8, *s8; + __m128i pp, m1, t1, t2, va; + + s8 = (uint8_t *) rd->s_start; + d8 = (uint8_t *) rd->d_start; + + pp = _mm_set1_epi8(btd->prim_poly&0xff); + m1 = _mm_set1_epi8((btd->mask1)&0xff); + + while (d8 < (uint8_t *) rd->d_top) { + va = _mm_load_si128 ((__m128i *)(s8)); + SSE_AB2(pp, m1, va, t1, t2); + SSE_AB2(pp, m1, va, t1, t2); + _mm_store_si128((__m128i *)d8, va); + d8 += 16; + s8 += 16; + } +} +#endif + +#ifdef INTEL_SSE2 +static +void +gf_w4_bytwo_b_sse_region_4_xor(gf_region_data *rd, struct gf_bytwo_data *btd) +{ + uint8_t *d8, *s8; + __m128i pp, m1, t1, t2, va, vb; + + s8 = (uint8_t *) rd->s_start; + d8 = (uint8_t *) rd->d_start; + + pp = _mm_set1_epi8(btd->prim_poly&0xff); + m1 = _mm_set1_epi8((btd->mask1)&0xff); + + while (d8 < (uint8_t *) rd->d_top) { + va = _mm_load_si128 ((__m128i *)(s8)); + SSE_AB2(pp, m1, va, t1, t2); + SSE_AB2(pp, m1, va, t1, t2); + vb = _mm_load_si128 ((__m128i *)(d8)); + vb = _mm_xor_si128(vb, va); + _mm_store_si128((__m128i *)d8, vb); + d8 += 16; + s8 += 16; + } +} +#endif + + +#ifdef INTEL_SSE2 +static +void +gf_w4_bytwo_b_sse_region_3_noxor(gf_region_data *rd, struct gf_bytwo_data *btd) +{ + uint8_t *d8, *s8; + __m128i pp, m1, t1, t2, va, vb; + + s8 = (uint8_t *) rd->s_start; + d8 = (uint8_t *) rd->d_start; + + pp = _mm_set1_epi8(btd->prim_poly&0xff); + m1 = _mm_set1_epi8((btd->mask1)&0xff); + + while (d8 < (uint8_t *) rd->d_top) { + va = _mm_load_si128 ((__m128i *)(s8)); + vb = va; + SSE_AB2(pp, m1, va, t1, t2); + va = _mm_xor_si128(va, vb); + _mm_store_si128((__m128i *)d8, va); + d8 += 16; + s8 += 16; + } +} +#endif + +#ifdef INTEL_SSE2 +static +void +gf_w4_bytwo_b_sse_region_3_xor(gf_region_data *rd, struct gf_bytwo_data *btd) +{ + uint8_t *d8, *s8; + __m128i pp, m1, t1, t2, va, vb; + + s8 = (uint8_t *) rd->s_start; + d8 = (uint8_t *) rd->d_start; + + pp = _mm_set1_epi8(btd->prim_poly&0xff); + m1 = _mm_set1_epi8((btd->mask1)&0xff); + + while (d8 < (uint8_t *) rd->d_top) { + va = _mm_load_si128 ((__m128i *)(s8)); + vb = _mm_xor_si128(_mm_load_si128 ((__m128i *)(d8)), va); + SSE_AB2(pp, m1, va, t1, t2); + vb = _mm_xor_si128(vb, va); + _mm_store_si128((__m128i *)d8, vb); + d8 += 16; + s8 += 16; + } +} +#endif + +#ifdef INTEL_SSE2 +static +void +gf_w4_bytwo_b_sse_region_5_noxor(gf_region_data *rd, struct gf_bytwo_data *btd) +{ + uint8_t *d8, *s8; + __m128i pp, m1, t1, t2, va, vb; + + s8 = (uint8_t *) rd->s_start; + d8 = (uint8_t *) rd->d_start; + + pp = _mm_set1_epi8(btd->prim_poly&0xff); + m1 = _mm_set1_epi8((btd->mask1)&0xff); + + while (d8 < (uint8_t *) rd->d_top) { + va = _mm_load_si128 ((__m128i *)(s8)); + vb = va; + SSE_AB2(pp, m1, va, t1, t2); + SSE_AB2(pp, m1, va, t1, t2); + va = _mm_xor_si128(va, vb); + _mm_store_si128((__m128i *)d8, va); + d8 += 16; + s8 += 16; + } +} +#endif + +#ifdef INTEL_SSE2 +static +void +gf_w4_bytwo_b_sse_region_5_xor(gf_region_data *rd, struct gf_bytwo_data *btd) +{ + uint8_t *d8, *s8; + __m128i pp, m1, t1, t2, va, vb; + + s8 = (uint8_t *) rd->s_start; + d8 = (uint8_t *) rd->d_start; + + pp = _mm_set1_epi8(btd->prim_poly&0xff); + m1 = _mm_set1_epi8((btd->mask1)&0xff); + + while (d8 < (uint8_t *) rd->d_top) { + va = _mm_load_si128 ((__m128i *)(s8)); + vb = _mm_xor_si128(_mm_load_si128 ((__m128i *)(d8)), va); + SSE_AB2(pp, m1, va, t1, t2); + SSE_AB2(pp, m1, va, t1, t2); + vb = _mm_xor_si128(vb, va); + _mm_store_si128((__m128i *)d8, vb); + d8 += 16; + s8 += 16; + } +} +#endif + +#ifdef INTEL_SSE2 +static +void +gf_w4_bytwo_b_sse_region_7_noxor(gf_region_data *rd, struct gf_bytwo_data *btd) +{ + uint8_t *d8, *s8; + __m128i pp, m1, t1, t2, va, vb; + + s8 = (uint8_t *) rd->s_start; + d8 = (uint8_t *) rd->d_start; + + pp = _mm_set1_epi8(btd->prim_poly&0xff); + m1 = _mm_set1_epi8((btd->mask1)&0xff); + + while (d8 < (uint8_t *) rd->d_top) { + va = _mm_load_si128 ((__m128i *)(s8)); + vb = va; + SSE_AB2(pp, m1, va, t1, t2); + vb = _mm_xor_si128(va, vb); + SSE_AB2(pp, m1, va, t1, t2); + va = _mm_xor_si128(va, vb); + _mm_store_si128((__m128i *)d8, va); + d8 += 16; + s8 += 16; + } +} +#endif + +#ifdef INTEL_SSE2 +static +void +gf_w4_bytwo_b_sse_region_7_xor(gf_region_data *rd, struct gf_bytwo_data *btd) +{ + uint8_t *d8, *s8; + __m128i pp, m1, t1, t2, va, vb; + + s8 = (uint8_t *) rd->s_start; + d8 = (uint8_t *) rd->d_start; + + pp = _mm_set1_epi8(btd->prim_poly&0xff); + m1 = _mm_set1_epi8((btd->mask1)&0xff); + + while (d8 < (uint8_t *) rd->d_top) { + va = _mm_load_si128 ((__m128i *)(s8)); + vb = _mm_xor_si128(_mm_load_si128 ((__m128i *)(d8)), va); + SSE_AB2(pp, m1, va, t1, t2); + vb = _mm_xor_si128(vb, va); + SSE_AB2(pp, m1, va, t1, t2); + vb = _mm_xor_si128(vb, va); + _mm_store_si128((__m128i *)d8, vb); + d8 += 16; + s8 += 16; + } +} +#endif + +#ifdef INTEL_SSE2 +static +void +gf_w4_bytwo_b_sse_region_6_noxor(gf_region_data *rd, struct gf_bytwo_data *btd) +{ + uint8_t *d8, *s8; + __m128i pp, m1, t1, t2, va, vb; + + s8 = (uint8_t *) rd->s_start; + d8 = (uint8_t *) rd->d_start; + + pp = _mm_set1_epi8(btd->prim_poly&0xff); + m1 = _mm_set1_epi8((btd->mask1)&0xff); + + while (d8 < (uint8_t *) rd->d_top) { + va = _mm_load_si128 ((__m128i *)(s8)); + SSE_AB2(pp, m1, va, t1, t2); + vb = va; + SSE_AB2(pp, m1, va, t1, t2); + va = _mm_xor_si128(va, vb); + _mm_store_si128((__m128i *)d8, va); + d8 += 16; + s8 += 16; + } +} +#endif + +#ifdef INTEL_SSE2 +static +void +gf_w4_bytwo_b_sse_region_6_xor(gf_region_data *rd, struct gf_bytwo_data *btd) +{ + uint8_t *d8, *s8; + __m128i pp, m1, t1, t2, va, vb; + + s8 = (uint8_t *) rd->s_start; + d8 = (uint8_t *) rd->d_start; + + pp = _mm_set1_epi8(btd->prim_poly&0xff); + m1 = _mm_set1_epi8((btd->mask1)&0xff); + + while (d8 < (uint8_t *) rd->d_top) { + va = _mm_load_si128 ((__m128i *)(s8)); + SSE_AB2(pp, m1, va, t1, t2); + vb = _mm_xor_si128(_mm_load_si128 ((__m128i *)(d8)), va); + SSE_AB2(pp, m1, va, t1, t2); + vb = _mm_xor_si128(vb, va); + _mm_store_si128((__m128i *)d8, vb); + d8 += 16; + s8 += 16; + } +} +#endif + +#ifdef INTEL_SSE2 +static +void +gf_w4_bytwo_b_sse_multiply_region(gf_t *gf, void *src, void *dest, gf_val_32_t val, int bytes, int xor) +{ + uint8_t *d8, *s8, tb; + __m128i pp, m1, m2, t1, t2, va, vb; + struct gf_bytwo_data *btd; + gf_region_data rd; + + if (val == 0) { gf_multby_zero(dest, bytes, xor); return; } + if (val == 1) { gf_multby_one(src, dest, bytes, xor); return; } + + gf_set_region_data(&rd, gf, src, dest, bytes, val, xor, 16); + gf_do_initial_region_alignment(&rd); + + s8 = (uint8_t *) rd.s_start; + d8 = (uint8_t *) rd.d_start; + + btd = (struct gf_bytwo_data *) ((gf_internal_t *) (gf->scratch))->private; + + switch (val) { + case 2: + if (!xor) { + gf_w4_bytwo_b_sse_region_2_noxor(&rd, btd); + } else { + gf_w4_bytwo_b_sse_region_2_xor(&rd, btd); + } + gf_do_final_region_alignment(&rd); + return; + case 3: + if (!xor) { + gf_w4_bytwo_b_sse_region_3_noxor(&rd, btd); + } else { + gf_w4_bytwo_b_sse_region_3_xor(&rd, btd); + } + gf_do_final_region_alignment(&rd); + return; + case 4: + if (!xor) { + gf_w4_bytwo_b_sse_region_4_noxor(&rd, btd); + } else { + gf_w4_bytwo_b_sse_region_4_xor(&rd, btd); + } + gf_do_final_region_alignment(&rd); + return; + case 5: + if (!xor) { + gf_w4_bytwo_b_sse_region_5_noxor(&rd, btd); + } else { + gf_w4_bytwo_b_sse_region_5_xor(&rd, btd); + } + gf_do_final_region_alignment(&rd); + return; + case 6: + if (!xor) { + gf_w4_bytwo_b_sse_region_6_noxor(&rd, btd); + } else { + gf_w4_bytwo_b_sse_region_6_xor(&rd, btd); + } + gf_do_final_region_alignment(&rd); + return; + case 7: + if (!xor) { + gf_w4_bytwo_b_sse_region_7_noxor(&rd, btd); + } else { + gf_w4_bytwo_b_sse_region_7_xor(&rd, btd); + } + gf_do_final_region_alignment(&rd); + return; + } + + pp = _mm_set1_epi8(btd->prim_poly&0xff); + m1 = _mm_set1_epi8((btd->mask1)&0xff); + m2 = _mm_set1_epi8((btd->mask2)&0xff); + + if (xor) { + while (d8 < (uint8_t *) rd.d_top) { + va = _mm_load_si128 ((__m128i *)(s8)); + vb = _mm_load_si128 ((__m128i *)(d8)); + tb = val; + while (1) { + if (tb & 1) vb = _mm_xor_si128(vb, va); + tb >>= 1; + if (tb == 0) break; + SSE_AB2(pp, m1, va, t1, t2); + } + _mm_store_si128((__m128i *)d8, vb); + d8 += 16; + s8 += 16; + } + } else { + while (d8 < (uint8_t *) rd.d_top) { + va = _mm_load_si128 ((__m128i *)(s8)); + vb = _mm_setzero_si128 (); + tb = val; + while (1) { + if (tb & 1) vb = _mm_xor_si128(vb, va); + tb >>= 1; + if (tb == 0) break; + t1 = _mm_and_si128(_mm_slli_epi64(va, 1), m1); + t2 = _mm_and_si128(va, m2); + t2 = _mm_sub_epi64 ( + _mm_slli_epi64(t2, 1), _mm_srli_epi64(t2, (GF_FIELD_WIDTH-1))); + va = _mm_xor_si128(t1, _mm_and_si128(t2, pp)); + } + _mm_store_si128((__m128i *)d8, vb); + d8 += 16; + s8 += 16; + } + } + gf_do_final_region_alignment(&rd); +} +#endif + +static +void +gf_w4_bytwo_b_nosse_multiply_region(gf_t *gf, void *src, void *dest, gf_val_32_t val, int bytes, int xor) +{ + uint64_t *s64, *d64, t1, t2, ta, tb, prod; + struct gf_bytwo_data *btd; + gf_region_data rd; + + if (val == 0) { gf_multby_zero(dest, bytes, xor); return; } + if (val == 1) { gf_multby_one(src, dest, bytes, xor); return; } + + gf_set_region_data(&rd, gf, src, dest, bytes, val, xor, 16); + gf_do_initial_region_alignment(&rd); + + btd = (struct gf_bytwo_data *) ((gf_internal_t *) (gf->scratch))->private; + s64 = (uint64_t *) rd.s_start; + d64 = (uint64_t *) rd.d_start; + + switch (val) { + case 1: + if (xor) { + while (d64 < (uint64_t *) rd.d_top) { + *d64 ^= *s64; + d64++; + s64++; + } + } else { + while (d64 < (uint64_t *) rd.d_top) { + *d64 = *s64; + d64++; + s64++; + } + } + break; + case 2: + if (xor) { + while (d64 < (uint64_t *) rd.d_top) { + ta = *s64; + AB2(btd->prim_poly, btd->mask1, btd->mask2, ta, t1, t2); + *d64 ^= ta; + d64++; + s64++; + } + } else { + while (d64 < (uint64_t *) rd.d_top) { + ta = *s64; + AB2(btd->prim_poly, btd->mask1, btd->mask2, ta, t1, t2); + *d64 = ta; + d64++; + s64++; + } + } + break; + case 3: + if (xor) { + while (d64 < (uint64_t *) rd.d_top) { + ta = *s64; + prod = ta; + AB2(btd->prim_poly, btd->mask1, btd->mask2, ta, t1, t2); + *d64 ^= (ta ^ prod); + d64++; + s64++; + } + } else { + while (d64 < (uint64_t *) rd.d_top) { + ta = *s64; + prod = ta; + AB2(btd->prim_poly, btd->mask1, btd->mask2, ta, t1, t2); + *d64 = (ta ^ prod); + d64++; + s64++; + } + } + break; + case 4: + if (xor) { + while (d64 < (uint64_t *) rd.d_top) { + ta = *s64; + AB2(btd->prim_poly, btd->mask1, btd->mask2, ta, t1, t2); + AB2(btd->prim_poly, btd->mask1, btd->mask2, ta, t1, t2); + *d64 ^= ta; + d64++; + s64++; + } + } else { + while (d64 < (uint64_t *) rd.d_top) { + ta = *s64; + AB2(btd->prim_poly, btd->mask1, btd->mask2, ta, t1, t2); + AB2(btd->prim_poly, btd->mask1, btd->mask2, ta, t1, t2); + *d64 = ta; + d64++; + s64++; + } + } + break; + case 5: + if (xor) { + while (d64 < (uint64_t *) rd.d_top) { + ta = *s64; + prod = ta; + AB2(btd->prim_poly, btd->mask1, btd->mask2, ta, t1, t2); + AB2(btd->prim_poly, btd->mask1, btd->mask2, ta, t1, t2); + *d64 ^= (ta ^ prod); + d64++; + s64++; + } + } else { + while (d64 < (uint64_t *) rd.d_top) { + ta = *s64; + prod = ta; + AB2(btd->prim_poly, btd->mask1, btd->mask2, ta, t1, t2); + AB2(btd->prim_poly, btd->mask1, btd->mask2, ta, t1, t2); + *d64 = ta ^ prod; + d64++; + s64++; + } + } + break; + case 6: + if (xor) { + while (d64 < (uint64_t *) rd.d_top) { + ta = *s64; + AB2(btd->prim_poly, btd->mask1, btd->mask2, ta, t1, t2); + prod = ta; + AB2(btd->prim_poly, btd->mask1, btd->mask2, ta, t1, t2); + *d64 ^= (ta ^ prod); + d64++; + s64++; + } + } else { + while (d64 < (uint64_t *) rd.d_top) { + ta = *s64; + AB2(btd->prim_poly, btd->mask1, btd->mask2, ta, t1, t2); + prod = ta; + AB2(btd->prim_poly, btd->mask1, btd->mask2, ta, t1, t2); + *d64 = ta ^ prod; + d64++; + s64++; + } + } + break; + case 7: + if (xor) { + while (d64 < (uint64_t *) rd.d_top) { + ta = *s64; + prod = ta; + AB2(btd->prim_poly, btd->mask1, btd->mask2, ta, t1, t2); + prod ^= ta; + AB2(btd->prim_poly, btd->mask1, btd->mask2, ta, t1, t2); + *d64 ^= (ta ^ prod); + d64++; + s64++; + } + } else { + while (d64 < (uint64_t *) rd.d_top) { + ta = *s64; + prod = ta; + AB2(btd->prim_poly, btd->mask1, btd->mask2, ta, t1, t2); + prod ^= ta; + AB2(btd->prim_poly, btd->mask1, btd->mask2, ta, t1, t2); + *d64 = ta ^ prod; + d64++; + s64++; + } + } + break; + case 8: + if (xor) { + while (d64 < (uint64_t *) rd.d_top) { + ta = *s64; + AB2(btd->prim_poly, btd->mask1, btd->mask2, ta, t1, t2); + AB2(btd->prim_poly, btd->mask1, btd->mask2, ta, t1, t2); + AB2(btd->prim_poly, btd->mask1, btd->mask2, ta, t1, t2); + *d64 ^= ta; + d64++; + s64++; + } + } else { + while (d64 < (uint64_t *) rd.d_top) { + ta = *s64; + AB2(btd->prim_poly, btd->mask1, btd->mask2, ta, t1, t2); + AB2(btd->prim_poly, btd->mask1, btd->mask2, ta, t1, t2); + AB2(btd->prim_poly, btd->mask1, btd->mask2, ta, t1, t2); + *d64 = ta; + d64++; + s64++; + } + } + break; + case 9: + if (xor) { + while (d64 < (uint64_t *) rd.d_top) { + ta = *s64; + prod = ta; + AB2(btd->prim_poly, btd->mask1, btd->mask2, ta, t1, t2); + AB2(btd->prim_poly, btd->mask1, btd->mask2, ta, t1, t2); + AB2(btd->prim_poly, btd->mask1, btd->mask2, ta, t1, t2); + *d64 ^= (ta ^ prod); + d64++; + s64++; + } + } else { + while (d64 < (uint64_t *) rd.d_top) { + ta = *s64; + prod = ta; + AB2(btd->prim_poly, btd->mask1, btd->mask2, ta, t1, t2); + AB2(btd->prim_poly, btd->mask1, btd->mask2, ta, t1, t2); + AB2(btd->prim_poly, btd->mask1, btd->mask2, ta, t1, t2); + *d64 = (ta ^ prod); + d64++; + s64++; + } + } + break; + case 10: + if (xor) { + while (d64 < (uint64_t *) rd.d_top) { + ta = *s64; + AB2(btd->prim_poly, btd->mask1, btd->mask2, ta, t1, t2); + prod = ta; + AB2(btd->prim_poly, btd->mask1, btd->mask2, ta, t1, t2); + AB2(btd->prim_poly, btd->mask1, btd->mask2, ta, t1, t2); + *d64 ^= (ta ^ prod); + d64++; + s64++; + } + } else { + while (d64 < (uint64_t *) rd.d_top) { + ta = *s64; + AB2(btd->prim_poly, btd->mask1, btd->mask2, ta, t1, t2); + prod = ta; + AB2(btd->prim_poly, btd->mask1, btd->mask2, ta, t1, t2); + AB2(btd->prim_poly, btd->mask1, btd->mask2, ta, t1, t2); + *d64 = (ta ^ prod); + d64++; + s64++; + } + } + break; + case 11: + if (xor) { + while (d64 < (uint64_t *) rd.d_top) { + ta = *s64; + prod = ta; + AB2(btd->prim_poly, btd->mask1, btd->mask2, ta, t1, t2); + prod ^= ta; + AB2(btd->prim_poly, btd->mask1, btd->mask2, ta, t1, t2); + AB2(btd->prim_poly, btd->mask1, btd->mask2, ta, t1, t2); + *d64 ^= (ta ^ prod); + d64++; + s64++; + } + } else { + while (d64 < (uint64_t *) rd.d_top) { + ta = *s64; + prod = ta; + AB2(btd->prim_poly, btd->mask1, btd->mask2, ta, t1, t2); + prod ^= ta; + AB2(btd->prim_poly, btd->mask1, btd->mask2, ta, t1, t2); + AB2(btd->prim_poly, btd->mask1, btd->mask2, ta, t1, t2); + *d64 = (ta ^ prod); + d64++; + s64++; + } + } + break; + case 12: + if (xor) { + while (d64 < (uint64_t *) rd.d_top) { + ta = *s64; + AB2(btd->prim_poly, btd->mask1, btd->mask2, ta, t1, t2); + AB2(btd->prim_poly, btd->mask1, btd->mask2, ta, t1, t2); + prod = ta; + AB2(btd->prim_poly, btd->mask1, btd->mask2, ta, t1, t2); + *d64 ^= (ta ^ prod); + d64++; + s64++; + } + } else { + while (d64 < (uint64_t *) rd.d_top) { + ta = *s64; + AB2(btd->prim_poly, btd->mask1, btd->mask2, ta, t1, t2); + AB2(btd->prim_poly, btd->mask1, btd->mask2, ta, t1, t2); + prod = ta; + AB2(btd->prim_poly, btd->mask1, btd->mask2, ta, t1, t2); + *d64 = (ta ^ prod); + d64++; + s64++; + } + } + break; + case 13: + if (xor) { + while (d64 < (uint64_t *) rd.d_top) { + ta = *s64; + prod = ta; + AB2(btd->prim_poly, btd->mask1, btd->mask2, ta, t1, t2); + AB2(btd->prim_poly, btd->mask1, btd->mask2, ta, t1, t2); + prod ^= ta; + AB2(btd->prim_poly, btd->mask1, btd->mask2, ta, t1, t2); + *d64 ^= (ta ^ prod); + d64++; + s64++; + } + } else { + while (d64 < (uint64_t *) rd.d_top) { + ta = *s64; + prod = ta; + AB2(btd->prim_poly, btd->mask1, btd->mask2, ta, t1, t2); + AB2(btd->prim_poly, btd->mask1, btd->mask2, ta, t1, t2); + prod ^= ta; + AB2(btd->prim_poly, btd->mask1, btd->mask2, ta, t1, t2); + *d64 = (ta ^ prod); + d64++; + s64++; + } + } + break; + case 14: + if (xor) { + while (d64 < (uint64_t *) rd.d_top) { + ta = *s64; + AB2(btd->prim_poly, btd->mask1, btd->mask2, ta, t1, t2); + prod = ta; + AB2(btd->prim_poly, btd->mask1, btd->mask2, ta, t1, t2); + prod ^= ta; + AB2(btd->prim_poly, btd->mask1, btd->mask2, ta, t1, t2); + *d64 ^= (ta ^ prod); + d64++; + s64++; + } + } else { + while (d64 < (uint64_t *) rd.d_top) { + ta = *s64; + AB2(btd->prim_poly, btd->mask1, btd->mask2, ta, t1, t2); + prod = ta; + AB2(btd->prim_poly, btd->mask1, btd->mask2, ta, t1, t2); + prod ^= ta; + AB2(btd->prim_poly, btd->mask1, btd->mask2, ta, t1, t2); + *d64 = (ta ^ prod); + d64++; + s64++; + } + } + break; + case 15: + if (xor) { + while (d64 < (uint64_t *) rd.d_top) { + ta = *s64; + prod = ta; + AB2(btd->prim_poly, btd->mask1, btd->mask2, ta, t1, t2); + prod ^= ta; + AB2(btd->prim_poly, btd->mask1, btd->mask2, ta, t1, t2); + prod ^= ta; + AB2(btd->prim_poly, btd->mask1, btd->mask2, ta, t1, t2); + *d64 ^= (ta ^ prod); + d64++; + s64++; + } + } else { + while (d64 < (uint64_t *) rd.d_top) { + ta = *s64; + prod = ta; + AB2(btd->prim_poly, btd->mask1, btd->mask2, ta, t1, t2); + prod ^= ta; + AB2(btd->prim_poly, btd->mask1, btd->mask2, ta, t1, t2); + prod ^= ta; + AB2(btd->prim_poly, btd->mask1, btd->mask2, ta, t1, t2); + *d64 = (ta ^ prod); + d64++; + s64++; + } + } + break; + default: + if (xor) { + while (d64 < (uint64_t *) rd.d_top) { + prod = *d64 ; + ta = *s64; + tb = val; + while (1) { + if (tb & 1) prod ^= ta; + tb >>= 1; + if (tb == 0) break; + AB2(btd->prim_poly, btd->mask1, btd->mask2, ta, t1, t2); + } + *d64 = prod; + d64++; + s64++; + } + } else { + while (d64 < (uint64_t *) rd.d_top) { + prod = 0 ; + ta = *s64; + tb = val; + while (1) { + if (tb & 1) prod ^= ta; + tb >>= 1; + if (tb == 0) break; + AB2(btd->prim_poly, btd->mask1, btd->mask2, ta, t1, t2); + } + *d64 = prod; + d64++; + s64++; + } + } + break; + } + gf_do_final_region_alignment(&rd); +} + +static +int gf_w4_bytwo_init(gf_t *gf) +{ + gf_internal_t *h; + uint64_t ip, m1, m2; + struct gf_bytwo_data *btd; + + h = (gf_internal_t *) gf->scratch; + btd = (struct gf_bytwo_data *) (h->private); + ip = h->prim_poly & 0xf; + m1 = 0xe; + m2 = 0x8; + btd->prim_poly = 0; + btd->mask1 = 0; + btd->mask2 = 0; + + while (ip != 0) { + btd->prim_poly |= ip; + btd->mask1 |= m1; + btd->mask2 |= m2; + ip <<= GF_FIELD_WIDTH; + m1 <<= GF_FIELD_WIDTH; + m2 <<= GF_FIELD_WIDTH; + } + + if (h->mult_type == GF_MULT_BYTWO_p) { + SET_FUNCTION(gf,multiply,w32,gf_w4_bytwo_p_multiply) + #ifdef INTEL_SSE2 + if (gf_cpu_supports_intel_sse2 && !(h->region_type & GF_REGION_NOSIMD)) { + SET_FUNCTION(gf,multiply_region,w32,gf_w4_bytwo_p_sse_multiply_region) + } else { + #endif + SET_FUNCTION(gf,multiply_region,w32,gf_w4_bytwo_p_nosse_multiply_region) + if (h->region_type & GF_REGION_SIMD) + return 0; + #ifdef INTEL_SSE2 + } + #endif + } else { + SET_FUNCTION(gf,multiply,w32,gf_w4_bytwo_b_multiply) + #ifdef INTEL_SSE2 + if (gf_cpu_supports_intel_sse2 && !(h->region_type & GF_REGION_NOSIMD)) { + SET_FUNCTION(gf,multiply_region,w32,gf_w4_bytwo_b_sse_multiply_region) + } else { + #endif + SET_FUNCTION(gf,multiply_region,w32,gf_w4_bytwo_b_nosse_multiply_region) + if (h->region_type & GF_REGION_SIMD) + return 0; + #ifdef INTEL_SSE2 + } + #endif + } + return 1; +} + + +static +int gf_w4_cfm_init(gf_t *gf) +{ +#if defined(INTEL_SSE4_PCLMUL) + if (gf_cpu_supports_intel_pclmul) { + SET_FUNCTION(gf,multiply,w32,gf_w4_clm_multiply) + return 1; + } +#elif defined(ARM_NEON) + if (gf_cpu_supports_arm_neon) { + return gf_w4_neon_cfm_init(gf); + } +#endif + return 0; +} + +static +int gf_w4_shift_init(gf_t *gf) +{ + SET_FUNCTION(gf,multiply,w32,gf_w4_shift_multiply) + return 1; +} + +/* JSP: I'm putting all error-checking into gf_error_check(), so you don't + have to do error checking in scratch_size or in init */ + +int gf_w4_scratch_size(int mult_type, int region_type, int divide_type, int arg1, int arg2) +{ + switch(mult_type) + { + case GF_MULT_BYTWO_p: + case GF_MULT_BYTWO_b: + return sizeof(gf_internal_t) + sizeof(struct gf_bytwo_data); + break; + case GF_MULT_DEFAULT: + case GF_MULT_TABLE: + if (region_type == GF_REGION_CAUCHY) { + return sizeof(gf_internal_t) + sizeof(struct gf_single_table_data) + 64; + } + + if (mult_type == GF_MULT_DEFAULT && + !(gf_cpu_supports_arm_neon || gf_cpu_supports_intel_ssse3)) + region_type = GF_REGION_DOUBLE_TABLE; + + if (region_type & GF_REGION_DOUBLE_TABLE) { + return sizeof(gf_internal_t) + sizeof(struct gf_double_table_data) + 64; + } else if (region_type & GF_REGION_QUAD_TABLE) { + if ((region_type & GF_REGION_LAZY) == 0) { + return sizeof(gf_internal_t) + sizeof(struct gf_quad_table_data) + 64; + } else { + return sizeof(gf_internal_t) + sizeof(struct gf_quad_table_lazy_data) + 64; + } + } else { + return sizeof(gf_internal_t) + sizeof(struct gf_single_table_data) + 64; + } + break; + + case GF_MULT_LOG_TABLE: + return sizeof(gf_internal_t) + sizeof(struct gf_logtable_data) + 64; + break; + case GF_MULT_CARRY_FREE: + return sizeof(gf_internal_t); + break; + case GF_MULT_SHIFT: + return sizeof(gf_internal_t); + break; + default: + return 0; + } + return 0; +} + +int +gf_w4_init (gf_t *gf) +{ + gf_internal_t *h; + + h = (gf_internal_t *) gf->scratch; + if (h->prim_poly == 0) h->prim_poly = 0x13; + h->prim_poly |= 0x10; + SET_FUNCTION(gf,multiply,w32,NULL) + SET_FUNCTION(gf,divide,w32,NULL) + SET_FUNCTION(gf,inverse,w32,NULL) + SET_FUNCTION(gf,multiply_region,w32,NULL) + SET_FUNCTION(gf,extract_word,w32,gf_w4_extract_word) + + switch(h->mult_type) { + case GF_MULT_CARRY_FREE: if (gf_w4_cfm_init(gf) == 0) return 0; break; + case GF_MULT_SHIFT: if (gf_w4_shift_init(gf) == 0) return 0; break; + case GF_MULT_BYTWO_p: + case GF_MULT_BYTWO_b: if (gf_w4_bytwo_init(gf) == 0) return 0; break; + case GF_MULT_LOG_TABLE: if (gf_w4_log_init(gf) == 0) return 0; break; + case GF_MULT_DEFAULT: + case GF_MULT_TABLE: if (gf_w4_table_init(gf) == 0) return 0; break; + default: return 0; + } + + if (h->divide_type == GF_DIVIDE_EUCLID) { + SET_FUNCTION(gf,divide,w32,gf_w4_divide_from_inverse) + SET_FUNCTION(gf,inverse,w32,gf_w4_euclid) + } else if (h->divide_type == GF_DIVIDE_MATRIX) { + SET_FUNCTION(gf,divide,w32,gf_w4_divide_from_inverse) + SET_FUNCTION(gf,inverse,w32,gf_w4_matrix) + } + + if (gf->divide.w32 == NULL) { + SET_FUNCTION(gf,divide,w32,gf_w4_divide_from_inverse) + if (gf->inverse.w32 == NULL) SET_FUNCTION(gf,inverse,w32,gf_w4_euclid) + } + + if (gf->inverse.w32 == NULL) SET_FUNCTION(gf,inverse,w32,gf_w4_inverse_from_divide) + + if (h->region_type == GF_REGION_CAUCHY) { + SET_FUNCTION(gf,multiply_region,w32,gf_wgen_cauchy_region) + SET_FUNCTION(gf,extract_word,w32,gf_wgen_extract_word) + } + + if (gf->multiply_region.w32 == NULL) { + SET_FUNCTION(gf,multiply_region,w32,gf_w4_multiply_region_from_single) + } + + return 1; +} + +/* Inline setup functions */ + +uint8_t *gf_w4_get_mult_table(gf_t *gf) +{ + gf_internal_t *h; + struct gf_single_table_data *std; + + h = (gf_internal_t *) gf->scratch; + if (gf->multiply.w32 == gf_w4_single_table_multiply) { + std = (struct gf_single_table_data *) h->private; + return (uint8_t *) std->mult; + } + return NULL; +} + +uint8_t *gf_w4_get_div_table(gf_t *gf) +{ + gf_internal_t *h; + struct gf_single_table_data *std; + + h = (gf_internal_t *) gf->scratch; + if (gf->multiply.w32 == gf_w4_single_table_multiply) { + std = (struct gf_single_table_data *) h->private; + return (uint8_t *) std->div; + } + return NULL; +} + diff --git a/IDA_new/gf-complete/src/gf_w64.c b/IDA_new/gf-complete/src/gf_w64.c new file mode 100644 index 0000000..69e55db --- /dev/null +++ b/IDA_new/gf-complete/src/gf_w64.c @@ -0,0 +1,2235 @@ +/* + * GF-Complete: A Comprehensive Open Source Library for Galois Field Arithmetic + * James S. Plank, Ethan L. Miller, Kevin M. Greenan, + * Benjamin A. Arnold, John A. Burnum, Adam W. Disney, Allen C. McBride. + * + * gf_w64.c + * + * Routines for 64-bit Galois fields + */ + +#include "gf_int.h" +#include +#include +#include "gf_w64.h" +#include "gf_cpu.h" + +static +inline +gf_val_64_t gf_w64_inverse_from_divide (gf_t *gf, gf_val_64_t a) +{ + return gf->divide.w64(gf, 1, a); +} + +#define MM_PRINT8(s, r) { uint8_t blah[16], ii; printf("%-12s", s); _mm_storeu_si128((__m128i *)blah, r); for (ii = 0; ii < 16; ii += 1) printf("%s%02x", (ii%4==0) ? " " : " ", blah[15-ii]); printf("\n"); } + +static +inline +gf_val_64_t gf_w64_divide_from_inverse (gf_t *gf, gf_val_64_t a, gf_val_64_t b) +{ + b = gf->inverse.w64(gf, b); + return gf->multiply.w64(gf, a, b); +} + +static +void +gf_w64_multiply_region_from_single(gf_t *gf, void *src, void *dest, gf_val_64_t val, int bytes, int +xor) +{ + uint32_t i; + gf_val_64_t *s64; + gf_val_64_t *d64; + + s64 = (gf_val_64_t *) src; + d64 = (gf_val_64_t *) dest; + + if (val == 0) { gf_multby_zero(dest, bytes, xor); return; } + if (val == 1) { gf_multby_one(src, dest, bytes, xor); return; } + + if (xor) { + for (i = 0; i < bytes/sizeof(gf_val_64_t); i++) { + d64[i] ^= gf->multiply.w64(gf, val, s64[i]); + } + } else { + for (i = 0; i < bytes/sizeof(gf_val_64_t); i++) { + d64[i] = gf->multiply.w64(gf, val, s64[i]); + } + } +} + +#if defined(INTEL_SSE4_PCLMUL) +static +void +gf_w64_clm_multiply_region_from_single_2(gf_t *gf, void *src, void *dest, gf_val_64_t val, int bytes, int +xor) +{ + gf_val_64_t *s64, *d64, *top; + gf_region_data rd; + + __m128i a, b; + __m128i result, r1; + __m128i prim_poly; + __m128i w; + __m128i m1, m3, m4; + gf_internal_t * h = gf->scratch; + + if (val == 0) { gf_multby_zero(dest, bytes, xor); return; } + if (val == 1) { gf_multby_one(src, dest, bytes, xor); return; } + + gf_set_region_data(&rd, gf, src, dest, bytes, val, xor, 16); + gf_do_initial_region_alignment(&rd); + + prim_poly = _mm_set_epi32(0, 0, 0, (uint32_t)(h->prim_poly & 0xffffffffULL)); + b = _mm_insert_epi64 (_mm_setzero_si128(), val, 0); + m1 = _mm_set_epi32(0, 0, 0, (uint32_t)0xffffffff); + m3 = _mm_slli_si128(m1, 8); + m4 = _mm_slli_si128(m3, 4); + + s64 = (gf_val_64_t *) rd.s_start; + d64 = (gf_val_64_t *) rd.d_start; + top = (gf_val_64_t *) rd.d_top; + + if (xor) { + while (d64 != top) { + a = _mm_load_si128((__m128i *) s64); + result = _mm_clmulepi64_si128 (a, b, 1); + + w = _mm_clmulepi64_si128 (_mm_and_si128(result, m4), prim_poly, 1); + result = _mm_xor_si128 (result, w); + w = _mm_clmulepi64_si128 (_mm_and_si128(result, m3), prim_poly, 1); + r1 = _mm_xor_si128 (result, w); + + result = _mm_clmulepi64_si128 (a, b, 0); + + w = _mm_clmulepi64_si128 (_mm_and_si128(result, m4), prim_poly, 1); + result = _mm_xor_si128 (result, w); + + w = _mm_clmulepi64_si128 (_mm_and_si128(result, m3), prim_poly, 1); + result = _mm_xor_si128 (result, w); + + result = _mm_unpacklo_epi64(result, r1); + + r1 = _mm_load_si128((__m128i *) d64); + result = _mm_xor_si128(r1, result); + _mm_store_si128((__m128i *) d64, result); + d64 += 2; + s64 += 2; + } + } else { + while (d64 != top) { + + a = _mm_load_si128((__m128i *) s64); + result = _mm_clmulepi64_si128 (a, b, 1); + + w = _mm_clmulepi64_si128 (_mm_and_si128(result, m4), prim_poly, 1); + result = _mm_xor_si128 (result, w); + w = _mm_clmulepi64_si128 (_mm_and_si128(result, m3), prim_poly, 1); + r1 = _mm_xor_si128 (result, w); + + result = _mm_clmulepi64_si128 (a, b, 0); + + w = _mm_clmulepi64_si128 (_mm_and_si128(result, m4), prim_poly, 1); + result = _mm_xor_si128 (result, w); + w = _mm_clmulepi64_si128 (_mm_and_si128(result, m3), prim_poly, 1); + result = _mm_xor_si128 (result, w); + + result = _mm_unpacklo_epi64(result, r1); + + _mm_store_si128((__m128i *) d64, result); + d64 += 2; + s64 += 2; + } + } + gf_do_final_region_alignment(&rd); +} +#endif + +#if defined(INTEL_SSE4_PCLMUL) +static +void +gf_w64_clm_multiply_region_from_single_4(gf_t *gf, void *src, void *dest, gf_val_64_t val, int bytes, int +xor) +{ + gf_val_64_t *s64, *d64, *top; + gf_region_data rd; + + __m128i a, b; + __m128i result, r1; + __m128i prim_poly; + __m128i w; + __m128i m1, m3, m4; + gf_internal_t * h = gf->scratch; + + if (val == 0) { gf_multby_zero(dest, bytes, xor); return; } + if (val == 1) { gf_multby_one(src, dest, bytes, xor); return; } + + gf_set_region_data(&rd, gf, src, dest, bytes, val, xor, 16); + gf_do_initial_region_alignment(&rd); + + prim_poly = _mm_set_epi32(0, 0, 0, (uint32_t)(h->prim_poly & 0xffffffffULL)); + b = _mm_insert_epi64 (_mm_setzero_si128(), val, 0); + m1 = _mm_set_epi32(0, 0, 0, (uint32_t)0xffffffff); + m3 = _mm_slli_si128(m1, 8); + m4 = _mm_slli_si128(m3, 4); + + s64 = (gf_val_64_t *) rd.s_start; + d64 = (gf_val_64_t *) rd.d_start; + top = (gf_val_64_t *) rd.d_top; + + if (xor) { + while (d64 != top) { + a = _mm_load_si128((__m128i *) s64); + result = _mm_clmulepi64_si128 (a, b, 1); + + w = _mm_clmulepi64_si128 (_mm_and_si128(result, m4), prim_poly, 1); + result = _mm_xor_si128 (result, w); + w = _mm_clmulepi64_si128 (_mm_and_si128(result, m3), prim_poly, 1); + r1 = _mm_xor_si128 (result, w); + + result = _mm_clmulepi64_si128 (a, b, 0); + + w = _mm_clmulepi64_si128 (_mm_and_si128(result, m4), prim_poly, 1); + result = _mm_xor_si128 (result, w); + + w = _mm_clmulepi64_si128 (_mm_and_si128(result, m3), prim_poly, 1); + result = _mm_xor_si128 (result, w); + + result = _mm_unpacklo_epi64(result, r1); + + r1 = _mm_load_si128((__m128i *) d64); + result = _mm_xor_si128(r1, result); + _mm_store_si128((__m128i *) d64, result); + d64 += 2; + s64 += 2; + } + } else { + while (d64 != top) { + a = _mm_load_si128((__m128i *) s64); + result = _mm_clmulepi64_si128 (a, b, 1); + + w = _mm_clmulepi64_si128 (_mm_and_si128(result, m4), prim_poly, 1); + result = _mm_xor_si128 (result, w); + w = _mm_clmulepi64_si128 (_mm_and_si128(result, m3), prim_poly, 1); + r1 = _mm_xor_si128 (result, w); + + result = _mm_clmulepi64_si128 (a, b, 0); + + w = _mm_clmulepi64_si128 (_mm_and_si128(result, m4), prim_poly, 1); + result = _mm_xor_si128 (result, w); + w = _mm_clmulepi64_si128 (_mm_and_si128(result, m3), prim_poly, 1); + result = _mm_xor_si128 (result, w); + + result = _mm_unpacklo_epi64(result, r1); + + _mm_store_si128((__m128i *) d64, result); + d64 += 2; + s64 += 2; + } + } + gf_do_final_region_alignment(&rd); +} +#endif + +static + inline +gf_val_64_t gf_w64_euclid (gf_t *gf, gf_val_64_t b) +{ + gf_val_64_t e_i, e_im1, e_ip1; + gf_val_64_t d_i, d_im1, d_ip1; + gf_val_64_t y_i, y_im1, y_ip1; + gf_val_64_t c_i; + gf_val_64_t one = 1; + + if (b == 0) return -1; + e_im1 = ((gf_internal_t *) (gf->scratch))->prim_poly; + e_i = b; + d_im1 = 64; + for (d_i = d_im1-1; ((one << d_i) & e_i) == 0; d_i--) ; + y_i = 1; + y_im1 = 0; + + while (e_i != 1) { + + e_ip1 = e_im1; + d_ip1 = d_im1; + c_i = 0; + + while (d_ip1 >= d_i) { + c_i ^= (one << (d_ip1 - d_i)); + e_ip1 ^= (e_i << (d_ip1 - d_i)); + d_ip1--; + if (e_ip1 == 0) return 0; + while ((e_ip1 & (one << d_ip1)) == 0) d_ip1--; + } + + y_ip1 = y_im1 ^ gf->multiply.w64(gf, c_i, y_i); + y_im1 = y_i; + y_i = y_ip1; + + e_im1 = e_i; + d_im1 = d_i; + e_i = e_ip1; + d_i = d_ip1; + } + + return y_i; +} + +/* JSP: GF_MULT_SHIFT: The world's dumbest multiplication algorithm. I only + include it for completeness. It does have the feature that it requires no + extra memory. +*/ + +static +inline +gf_val_64_t +gf_w64_shift_multiply (gf_t *gf, gf_val_64_t a64, gf_val_64_t b64) +{ + uint64_t pl, pr, ppl, ppr, i, a, bl, br, one, lbit; + gf_internal_t *h; + + h = (gf_internal_t *) gf->scratch; + + /* Allen: set leading one of primitive polynomial */ + + a = a64; + bl = 0; + br = b64; + one = 1; + lbit = (one << 63); + + pl = 0; /* Allen: left side of product */ + pr = 0; /* Allen: right side of product */ + + /* Allen: unlike the corresponding functions for smaller word sizes, + * this loop carries out the initial carryless multiply by + * shifting b itself rather than simply looking at successively + * higher shifts of b */ + + for (i = 0; i < GF_FIELD_WIDTH; i++) { + if (a & (one << i)) { + pl ^= bl; + pr ^= br; + } + + bl <<= 1; + if (br & lbit) bl ^= 1; + br <<= 1; + } + + /* Allen: the name of the variable "one" is no longer descriptive at this point */ + + one = lbit >> 1; + ppl = (h->prim_poly >> 2) | one; + ppr = (h->prim_poly << (GF_FIELD_WIDTH-2)); + while (one != 0) { + if (pl & one) { + pl ^= ppl; + pr ^= ppr; + } + one >>= 1; + ppr >>= 1; + if (ppl & 1) ppr ^= lbit; + ppl >>= 1; + } + return pr; +} + +/* + * ELM: Use the Intel carryless multiply instruction to do very fast 64x64 multiply. + */ + +#if defined(INTEL_SSE4_PCLMUL) + +static +inline +gf_val_64_t +gf_w64_clm_multiply_2 (gf_t *gf, gf_val_64_t a64, gf_val_64_t b64) +{ + gf_val_64_t rv = 0; + + __m128i a, b; + __m128i result; + __m128i prim_poly; + __m128i v, w; + gf_internal_t * h = gf->scratch; + + a = _mm_insert_epi64 (_mm_setzero_si128(), a64, 0); + b = _mm_insert_epi64 (a, b64, 0); + prim_poly = _mm_set_epi32(0, 0, 0, (uint32_t)(h->prim_poly & 0xffffffffULL)); + /* Do the initial multiply */ + + result = _mm_clmulepi64_si128 (a, b, 0); + + /* Mask off the high order 32 bits using subtraction of the polynomial. + * NOTE: this part requires that the polynomial have at least 32 leading 0 bits. + */ + + /* Adam: We cant include the leading one in the 64 bit pclmul, + so we need to split up the high 8 bytes of the result into two + parts before we multiply them with the prim_poly.*/ + + v = _mm_insert_epi32 (_mm_srli_si128 (result, 8), 0, 0); + w = _mm_clmulepi64_si128 (prim_poly, v, 0); + result = _mm_xor_si128 (result, w); + v = _mm_insert_epi32 (_mm_srli_si128 (result, 8), 0, 1); + w = _mm_clmulepi64_si128 (prim_poly, v, 0); + result = _mm_xor_si128 (result, w); + + rv = ((gf_val_64_t)_mm_extract_epi64(result, 0)); + return rv; +} +#endif + +#if defined(INTEL_SSE4_PCLMUL) + +static +inline +gf_val_64_t +gf_w64_clm_multiply_4 (gf_t *gf, gf_val_64_t a64, gf_val_64_t b64) +{ + gf_val_64_t rv = 0; + + __m128i a, b; + __m128i result; + __m128i prim_poly; + __m128i v, w; + gf_internal_t * h = gf->scratch; + + a = _mm_insert_epi64 (_mm_setzero_si128(), a64, 0); + b = _mm_insert_epi64 (a, b64, 0); + prim_poly = _mm_set_epi32(0, 0, 0, (uint32_t)(h->prim_poly & 0xffffffffULL)); + + /* Do the initial multiply */ + + result = _mm_clmulepi64_si128 (a, b, 0); + + v = _mm_insert_epi32 (_mm_srli_si128 (result, 8), 0, 0); + w = _mm_clmulepi64_si128 (prim_poly, v, 0); + result = _mm_xor_si128 (result, w); + v = _mm_insert_epi32 (_mm_srli_si128 (result, 8), 0, 1); + w = _mm_clmulepi64_si128 (prim_poly, v, 0); + result = _mm_xor_si128 (result, w); + + v = _mm_insert_epi32 (_mm_srli_si128 (result, 8), 0, 0); + w = _mm_clmulepi64_si128 (prim_poly, v, 0); + result = _mm_xor_si128 (result, w); + v = _mm_insert_epi32 (_mm_srli_si128 (result, 8), 0, 1); + w = _mm_clmulepi64_si128 (prim_poly, v, 0); + result = _mm_xor_si128 (result, w); + + rv = ((gf_val_64_t)_mm_extract_epi64(result, 0)); + return rv; +} +#endif + + +#if defined(INTEL_SSE4_PCLMUL) + void +gf_w64_clm_multiply_region(gf_t *gf, void *src, void *dest, uint64_t val, int bytes, int xor) +{ + gf_internal_t *h; + uint8_t *s8, *d8, *dtop; + gf_region_data rd; + __m128i v, b, m, prim_poly, c, fr, w, result; + + if (val == 0) { gf_multby_zero(dest, bytes, xor); return; } + if (val == 1) { gf_multby_one(src, dest, bytes, xor); return; } + + h = (gf_internal_t *) gf->scratch; + + gf_set_region_data(&rd, gf, src, dest, bytes, val, xor, 16); + gf_do_initial_region_alignment(&rd); + + s8 = (uint8_t *) rd.s_start; + d8 = (uint8_t *) rd.d_start; + dtop = (uint8_t *) rd.d_top; + + v = _mm_insert_epi64(_mm_setzero_si128(), val, 0); + m = _mm_set_epi32(0, 0, 0xffffffff, 0xffffffff); + prim_poly = _mm_set_epi32(0, 0, 0, (uint32_t)(h->prim_poly & 0xffffffffULL)); + + if (xor) { + while (d8 != dtop) { + b = _mm_load_si128((__m128i *) s8); + result = _mm_clmulepi64_si128 (b, v, 0); + c = _mm_insert_epi32 (_mm_srli_si128 (result, 8), 0, 0); + w = _mm_clmulepi64_si128 (prim_poly, c, 0); + result = _mm_xor_si128 (result, w); + c = _mm_insert_epi32 (_mm_srli_si128 (result, 8), 0, 1); + w = _mm_clmulepi64_si128 (prim_poly, c, 0); + fr = _mm_xor_si128 (result, w); + fr = _mm_and_si128 (fr, m); + + result = _mm_clmulepi64_si128 (b, v, 1); + c = _mm_insert_epi32 (_mm_srli_si128 (result, 8), 0, 0); + w = _mm_clmulepi64_si128 (prim_poly, c, 0); + result = _mm_xor_si128 (result, w); + c = _mm_insert_epi32 (_mm_srli_si128 (result, 8), 0, 1); + w = _mm_clmulepi64_si128 (prim_poly, c, 0); + result = _mm_xor_si128 (result, w); + result = _mm_slli_si128 (result, 8); + fr = _mm_xor_si128 (result, fr); + result = _mm_load_si128((__m128i *) d8); + fr = _mm_xor_si128 (result, fr); + + _mm_store_si128((__m128i *) d8, fr); + d8 += 16; + s8 += 16; + } + } else { + while (d8 < dtop) { + b = _mm_load_si128((__m128i *) s8); + result = _mm_clmulepi64_si128 (b, v, 0); + c = _mm_insert_epi32 (_mm_srli_si128 (result, 8), 0, 0); + w = _mm_clmulepi64_si128 (prim_poly, c, 0); + result = _mm_xor_si128 (result, w); + c = _mm_insert_epi32 (_mm_srli_si128 (result, 8), 0, 1); + w = _mm_clmulepi64_si128 (prim_poly, c, 0); + fr = _mm_xor_si128 (result, w); + fr = _mm_and_si128 (fr, m); + + result = _mm_clmulepi64_si128 (b, v, 1); + c = _mm_insert_epi32 (_mm_srli_si128 (result, 8), 0, 0); + w = _mm_clmulepi64_si128 (prim_poly, c, 0); + result = _mm_xor_si128 (result, w); + c = _mm_insert_epi32 (_mm_srli_si128 (result, 8), 0, 1); + w = _mm_clmulepi64_si128 (prim_poly, c, 0); + result = _mm_xor_si128 (result, w); + result = _mm_slli_si128 (result, 8); + fr = _mm_xor_si128 (result, fr); + + _mm_store_si128((__m128i *) d8, fr); + d8 += 16; + s8 += 16; + } + } + gf_do_final_region_alignment(&rd); +} +#endif + +void +gf_w64_split_4_64_lazy_multiply_region(gf_t *gf, void *src, void *dest, uint64_t val, int bytes, int xor) +{ + gf_internal_t *h; + struct gf_split_4_64_lazy_data *ld; + int i, j, k; + uint64_t pp, v, s, *s64, *d64, *top; + gf_region_data rd; + + if (val == 0) { gf_multby_zero(dest, bytes, xor); return; } + if (val == 1) { gf_multby_one(src, dest, bytes, xor); return; } + + h = (gf_internal_t *) gf->scratch; + pp = h->prim_poly; + + ld = (struct gf_split_4_64_lazy_data *) h->private; + + gf_set_region_data(&rd, gf, src, dest, bytes, val, xor, 8); + gf_do_initial_region_alignment(&rd); + + if (ld->last_value != val) { + v = val; + for (i = 0; i < 16; i++) { + ld->tables[i][0] = 0; + for (j = 1; j < 16; j <<= 1) { + for (k = 0; k < j; k++) { + ld->tables[i][k^j] = (v ^ ld->tables[i][k]); + } + v = (v & GF_FIRST_BIT) ? ((v << 1) ^ pp) : (v << 1); + } + } + } + ld->last_value = val; + + s64 = (uint64_t *) rd.s_start; + d64 = (uint64_t *) rd.d_start; + top = (uint64_t *) rd.d_top; + + while (d64 != top) { + v = (xor) ? *d64 : 0; + s = *s64; + i = 0; + while (s != 0) { + v ^= ld->tables[i][s&0xf]; + s >>= 4; + i++; + } + *d64 = v; + d64++; + s64++; + } + gf_do_final_region_alignment(&rd); +} + +static +inline +uint64_t +gf_w64_split_8_8_multiply (gf_t *gf, uint64_t a64, uint64_t b64) +{ + uint64_t product, i, j, mask, tb; + gf_internal_t *h; + struct gf_split_8_8_data *d8; + + h = (gf_internal_t *) gf->scratch; + d8 = (struct gf_split_8_8_data *) h->private; + product = 0; + mask = 0xff; + + for (i = 0; a64 != 0; i++) { + tb = b64; + for (j = 0; tb != 0; j++) { + product ^= d8->tables[i+j][a64&mask][tb&mask]; + tb >>= 8; + } + a64 >>= 8; + } + return product; +} + +void +gf_w64_split_8_64_lazy_multiply_region(gf_t *gf, void *src, void *dest, uint64_t val, int bytes, int xor) +{ + gf_internal_t *h; + struct gf_split_8_64_lazy_data *ld; + int i, j, k; + uint64_t pp, v, s, *s64, *d64, *top; + gf_region_data rd; + + if (val == 0) { gf_multby_zero(dest, bytes, xor); return; } + if (val == 1) { gf_multby_one(src, dest, bytes, xor); return; } + + h = (gf_internal_t *) gf->scratch; + pp = h->prim_poly; + + ld = (struct gf_split_8_64_lazy_data *) h->private; + + gf_set_region_data(&rd, gf, src, dest, bytes, val, xor, 4); + gf_do_initial_region_alignment(&rd); + + if (ld->last_value != val) { + v = val; + for (i = 0; i < 8; i++) { + ld->tables[i][0] = 0; + for (j = 1; j < 256; j <<= 1) { + for (k = 0; k < j; k++) { + ld->tables[i][k^j] = (v ^ ld->tables[i][k]); + } + v = (v & GF_FIRST_BIT) ? ((v << 1) ^ pp) : (v << 1); + } + } + } + ld->last_value = val; + + s64 = (uint64_t *) rd.s_start; + d64 = (uint64_t *) rd.d_start; + top = (uint64_t *) rd.d_top; + + while (d64 != top) { + v = (xor) ? *d64 : 0; + s = *s64; + i = 0; + while (s != 0) { + v ^= ld->tables[i][s&0xff]; + s >>= 8; + i++; + } + *d64 = v; + d64++; + s64++; + } + gf_do_final_region_alignment(&rd); +} + +void +gf_w64_split_16_64_lazy_multiply_region(gf_t *gf, void *src, void *dest, uint64_t val, int bytes, int xor) +{ + gf_internal_t *h; + struct gf_split_16_64_lazy_data *ld; + int i, j, k; + uint64_t pp, v, s, *s64, *d64, *top; + gf_region_data rd; + + if (val == 0) { gf_multby_zero(dest, bytes, xor); return; } + if (val == 1) { gf_multby_one(src, dest, bytes, xor); return; } + + h = (gf_internal_t *) gf->scratch; + pp = h->prim_poly; + + ld = (struct gf_split_16_64_lazy_data *) h->private; + + gf_set_region_data(&rd, gf, src, dest, bytes, val, xor, 4); + gf_do_initial_region_alignment(&rd); + + if (ld->last_value != val) { + v = val; + for (i = 0; i < 4; i++) { + ld->tables[i][0] = 0; + for (j = 1; j < (1<<16); j <<= 1) { + for (k = 0; k < j; k++) { + ld->tables[i][k^j] = (v ^ ld->tables[i][k]); + } + v = (v & GF_FIRST_BIT) ? ((v << 1) ^ pp) : (v << 1); + } + } + } + ld->last_value = val; + + s64 = (uint64_t *) rd.s_start; + d64 = (uint64_t *) rd.d_start; + top = (uint64_t *) rd.d_top; + + while (d64 != top) { + v = (xor) ? *d64 : 0; + s = *s64; + i = 0; + while (s != 0) { + v ^= ld->tables[i][s&0xffff]; + s >>= 16; + i++; + } + *d64 = v; + d64++; + s64++; + } + gf_do_final_region_alignment(&rd); +} + +static +int gf_w64_shift_init(gf_t *gf) +{ + SET_FUNCTION(gf,multiply,w64,gf_w64_shift_multiply) + SET_FUNCTION(gf,inverse,w64,gf_w64_euclid) + SET_FUNCTION(gf,multiply_region,w64,gf_w64_multiply_region_from_single) + return 1; +} + +static +int gf_w64_cfm_init(gf_t *gf) +{ + SET_FUNCTION(gf,inverse,w64,gf_w64_euclid) + SET_FUNCTION(gf,multiply_region,w64,gf_w64_multiply_region_from_single) + +#if defined(INTEL_SSE4_PCLMUL) + if (gf_cpu_supports_intel_pclmul) { + gf_internal_t *h; + + h = (gf_internal_t *) gf->scratch; + + if ((0xfffffffe00000000ULL & h->prim_poly) == 0){ + SET_FUNCTION(gf,multiply,w64,gf_w64_clm_multiply_2) + SET_FUNCTION(gf,multiply_region,w64,gf_w64_clm_multiply_region_from_single_2) + }else if((0xfffe000000000000ULL & h->prim_poly) == 0){ + SET_FUNCTION(gf,multiply,w64,gf_w64_clm_multiply_4) + SET_FUNCTION(gf,multiply_region,w64,gf_w64_clm_multiply_region_from_single_4) + } else { + return 0; + } + return 1; + } +#endif + + return 0; +} + +static +void +gf_w64_group_set_shift_tables(uint64_t *shift, uint64_t val, gf_internal_t *h) +{ + uint64_t i; + uint64_t j; + uint64_t one = 1; + int g_s; + + g_s = h->arg1; + shift[0] = 0; + + for (i = 1; i < ((uint64_t)1 << g_s); i <<= 1) { + for (j = 0; j < i; j++) shift[i|j] = shift[j]^val; + if (val & (one << 63)) { + val <<= 1; + val ^= h->prim_poly; + } else { + val <<= 1; + } + } +} + +static +inline +gf_val_64_t +gf_w64_group_multiply(gf_t *gf, gf_val_64_t a, gf_val_64_t b) +{ + uint64_t top, bot, mask, tp; + int g_s, g_r, lshift, rshift; + struct gf_w64_group_data *gd; + + gf_internal_t *h = (gf_internal_t *) gf->scratch; + g_s = h->arg1; + g_r = h->arg2; + gd = (struct gf_w64_group_data *) h->private; + gf_w64_group_set_shift_tables(gd->shift, b, h); + + mask = (((uint64_t)1 << g_s) - 1); + top = 0; + bot = gd->shift[a&mask]; + a >>= g_s; + + if (a == 0) return bot; + lshift = 0; + rshift = 64; + + do { /* Shifting out is straightfoward */ + lshift += g_s; + rshift -= g_s; + tp = gd->shift[a&mask]; + top ^= (tp >> rshift); + bot ^= (tp << lshift); + a >>= g_s; + } while (a != 0); + + /* Reducing is a bit gross, because I don't zero out the index bits of top. + The reason is that we throw top away. Even better, that last (tp >> rshift) + is going to be ignored, so it doesn't matter how (tp >> 64) is implemented. */ + + lshift = ((lshift-1) / g_r) * g_r; + rshift = 64 - lshift; + mask = ((uint64_t)1 << g_r) - 1; + while (lshift >= 0) { + tp = gd->reduce[(top >> lshift) & mask]; + top ^= (tp >> rshift); + bot ^= (tp << lshift); + lshift -= g_r; + rshift += g_r; + } + + return bot; +} + +static +void gf_w64_group_multiply_region(gf_t *gf, void *src, void *dest, gf_val_64_t val, int bytes, int xor) +{ + int i, fzb; + uint64_t a64, smask, rmask, top, bot, tp; + int lshift, rshift, g_s, g_r; + gf_region_data rd; + uint64_t *s64, *d64, *dtop; + struct gf_w64_group_data *gd; + gf_internal_t *h = (gf_internal_t *) gf->scratch; + + if (val == 0) { gf_multby_zero(dest, bytes, xor); return; } + if (val == 1) { gf_multby_one(src, dest, bytes, xor); return; } + + gd = (struct gf_w64_group_data *) h->private; + g_s = h->arg1; + g_r = h->arg2; + gf_w64_group_set_shift_tables(gd->shift, val, h); + + for (i = 63; !(val & (1ULL << i)); i--) ; + i += g_s; + + /* i is the bit position of the first zero bit in any element of + gd->shift[] */ + + if (i > 64) i = 64; + + fzb = i; + + gf_set_region_data(&rd, gf, src, dest, bytes, val, xor, 4); + + gf_do_initial_region_alignment(&rd); + + s64 = (uint64_t *) rd.s_start; + d64 = (uint64_t *) rd.d_start; + dtop = (uint64_t *) rd.d_top; + + smask = ((uint64_t)1 << g_s) - 1; + rmask = ((uint64_t)1 << g_r) - 1; + + while (d64 < dtop) { + a64 = *s64; + + top = 0; + bot = gd->shift[a64&smask]; + a64 >>= g_s; + i = fzb; + + if (a64 != 0) { + lshift = 0; + rshift = 64; + + do { + lshift += g_s; + rshift -= g_s; + tp = gd->shift[a64&smask]; + top ^= (tp >> rshift); + bot ^= (tp << lshift); + a64 >>= g_s; + } while (a64 != 0); + i += lshift; + + lshift = ((i-64-1) / g_r) * g_r; + rshift = 64 - lshift; + while (lshift >= 0) { + tp = gd->reduce[(top >> lshift) & rmask]; + top ^= (tp >> rshift); + bot ^= (tp << lshift); + lshift -= g_r; + rshift += g_r; + } + } + + if (xor) bot ^= *d64; + *d64 = bot; + d64++; + s64++; + } + gf_do_final_region_alignment(&rd); +} + +static +inline +gf_val_64_t +gf_w64_group_s_equals_r_multiply(gf_t *gf, gf_val_64_t a, gf_val_64_t b) +{ + int leftover, rs; + uint64_t p, l, ind, a64; + int bits_left; + int g_s; + + struct gf_w64_group_data *gd; + gf_internal_t *h = (gf_internal_t *) gf->scratch; + g_s = h->arg1; + + gd = (struct gf_w64_group_data *) h->private; + gf_w64_group_set_shift_tables(gd->shift, b, h); + + leftover = 64 % g_s; + if (leftover == 0) leftover = g_s; + + rs = 64 - leftover; + a64 = a; + ind = a64 >> rs; + a64 <<= leftover; + p = gd->shift[ind]; + + bits_left = rs; + rs = 64 - g_s; + + while (bits_left > 0) { + bits_left -= g_s; + ind = a64 >> rs; + a64 <<= g_s; + l = p >> rs; + p = (gd->shift[ind] ^ gd->reduce[l] ^ (p << g_s)); + } + return p; +} + +static +void gf_w64_group_s_equals_r_multiply_region(gf_t *gf, void *src, void *dest, gf_val_64_t val, int bytes, int xor) +{ + int leftover, rs; + uint64_t p, l, ind, a64; + int bits_left; + int g_s; + gf_region_data rd; + uint64_t *s64, *d64, *top; + struct gf_w64_group_data *gd; + gf_internal_t *h = (gf_internal_t *) gf->scratch; + + if (val == 0) { gf_multby_zero(dest, bytes, xor); return; } + if (val == 1) { gf_multby_one(src, dest, bytes, xor); return; } + + gd = (struct gf_w64_group_data *) h->private; + g_s = h->arg1; + gf_w64_group_set_shift_tables(gd->shift, val, h); + + gf_set_region_data(&rd, gf, src, dest, bytes, val, xor, 4); + gf_do_initial_region_alignment(&rd); + + s64 = (uint64_t *) rd.s_start; + d64 = (uint64_t *) rd.d_start; + top = (uint64_t *) rd.d_top; + + leftover = 64 % g_s; + if (leftover == 0) leftover = g_s; + + while (d64 < top) { + rs = 64 - leftover; + a64 = *s64; + ind = a64 >> rs; + a64 <<= leftover; + p = gd->shift[ind]; + + bits_left = rs; + rs = 64 - g_s; + + while (bits_left > 0) { + bits_left -= g_s; + ind = a64 >> rs; + a64 <<= g_s; + l = p >> rs; + p = (gd->shift[ind] ^ gd->reduce[l] ^ (p << g_s)); + } + if (xor) p ^= *d64; + *d64 = p; + d64++; + s64++; + } + gf_do_final_region_alignment(&rd); +} + + +static +int gf_w64_group_init(gf_t *gf) +{ + uint64_t i, j, p, index; + struct gf_w64_group_data *gd; + gf_internal_t *h = (gf_internal_t *) gf->scratch; + uint64_t g_r, g_s; + + g_s = h->arg1; + g_r = h->arg2; + + gd = (struct gf_w64_group_data *) h->private; + gd->shift = (uint64_t *) (&(gd->memory)); + gd->reduce = gd->shift + (1 << g_s); + + gd->reduce[0] = 0; + for (i = 0; i < ((uint64_t)1 << g_r); i++) { + p = 0; + index = 0; + for (j = 0; j < g_r; j++) { + if (i & (1 << j)) { + p ^= (h->prim_poly << j); + index ^= (1 << j); + if (j > 0) index ^= (h->prim_poly >> (64-j)); + } + } + gd->reduce[index] = p; + } + + if (g_s == g_r) { + SET_FUNCTION(gf,multiply,w64,gf_w64_group_s_equals_r_multiply) + SET_FUNCTION(gf,multiply_region,w64,gf_w64_group_s_equals_r_multiply_region) + } else { + SET_FUNCTION(gf,multiply,w64,gf_w64_group_multiply) + SET_FUNCTION(gf,multiply_region,w64,gf_w64_group_multiply_region) + } + SET_FUNCTION(gf,divide,w64,NULL) + SET_FUNCTION(gf,inverse,w64,gf_w64_euclid) + + return 1; +} + +static +gf_val_64_t gf_w64_extract_word(gf_t *gf, void *start, int bytes, int index) +{ + uint64_t *r64, rv; + + r64 = (uint64_t *) start; + rv = r64[index]; + return rv; +} + +static +gf_val_64_t gf_w64_composite_extract_word(gf_t *gf, void *start, int bytes, int index) +{ + int sub_size; + gf_internal_t *h; + uint8_t *r8, *top; + uint64_t a, b, *r64; + gf_region_data rd; + + h = (gf_internal_t *) gf->scratch; + gf_set_region_data(&rd, gf, start, start, bytes, 0, 0, 32); + r64 = (uint64_t *) start; + if (r64 + index < (uint64_t *) rd.d_start) return r64[index]; + if (r64 + index >= (uint64_t *) rd.d_top) return r64[index]; + index -= (((uint64_t *) rd.d_start) - r64); + r8 = (uint8_t *) rd.d_start; + top = (uint8_t *) rd.d_top; + sub_size = (top-r8)/2; + + a = h->base_gf->extract_word.w32(h->base_gf, r8, sub_size, index); + b = h->base_gf->extract_word.w32(h->base_gf, r8+sub_size, sub_size, index); + return (a | ((uint64_t)b << 32)); +} + +static +gf_val_64_t gf_w64_split_extract_word(gf_t *gf, void *start, int bytes, int index) +{ + int i; + uint64_t *r64, rv; + uint8_t *r8; + gf_region_data rd; + + gf_set_region_data(&rd, gf, start, start, bytes, 0, 0, 128); + r64 = (uint64_t *) start; + if (r64 + index < (uint64_t *) rd.d_start) return r64[index]; + if (r64 + index >= (uint64_t *) rd.d_top) return r64[index]; + index -= (((uint64_t *) rd.d_start) - r64); + r8 = (uint8_t *) rd.d_start; + r8 += ((index & 0xfffffff0)*8); + r8 += (index & 0xf); + r8 += 112; + rv =0; + for (i = 0; i < 8; i++) { + rv <<= 8; + rv |= *r8; + r8 -= 16; + } + return rv; +} + +static +inline +gf_val_64_t +gf_w64_bytwo_b_multiply (gf_t *gf, gf_val_64_t a, gf_val_64_t b) +{ + uint64_t prod, pp, bmask; + gf_internal_t *h; + + h = (gf_internal_t *) gf->scratch; + pp = h->prim_poly; + + prod = 0; + bmask = 0x8000000000000000ULL; + + while (1) { + if (a & 1) prod ^= b; + a >>= 1; + if (a == 0) return prod; + if (b & bmask) { + b = ((b << 1) ^ pp); + } else { + b <<= 1; + } + } +} + +static +inline +gf_val_64_t +gf_w64_bytwo_p_multiply (gf_t *gf, gf_val_64_t a, gf_val_64_t b) +{ + uint64_t prod, pp, pmask, amask; + gf_internal_t *h; + + h = (gf_internal_t *) gf->scratch; + pp = h->prim_poly; + + prod = 0; + + /* changed from declare then shift to just declare.*/ + + pmask = 0x8000000000000000ULL; + amask = 0x8000000000000000ULL; + + while (amask != 0) { + if (prod & pmask) { + prod = ((prod << 1) ^ pp); + } else { + prod <<= 1; + } + if (a & amask) prod ^= b; + amask >>= 1; + } + return prod; +} + +static +void +gf_w64_bytwo_p_nosse_multiply_region(gf_t *gf, void *src, void *dest, gf_val_64_t val, int bytes, int xor) +{ + uint64_t *s64, *d64, ta, prod, amask, pmask, pp; + gf_region_data rd; + gf_internal_t *h; + + if (val == 0) { gf_multby_zero(dest, bytes, xor); return; } + if (val == 1) { gf_multby_one(src, dest, bytes, xor); return; } + + gf_set_region_data(&rd, gf, src, dest, bytes, val, xor, 8); + gf_do_initial_region_alignment(&rd); + + h = (gf_internal_t *) gf->scratch; + + s64 = (uint64_t *) rd.s_start; + d64 = (uint64_t *) rd.d_start; + pmask = 0x80000000; + pmask <<= 32; + pp = h->prim_poly; + + if (xor) { + while (s64 < (uint64_t *) rd.s_top) { + prod = 0; + amask = pmask; + ta = *s64; + while (amask != 0) { + prod = (prod & pmask) ? ((prod << 1) ^ pp) : (prod << 1); + if (val & amask) prod ^= ta; + amask >>= 1; + } + *d64 ^= prod; + d64++; + s64++; + } + } else { + while (s64 < (uint64_t *) rd.s_top) { + prod = 0; + amask = pmask; + ta = *s64; + while (amask != 0) { + prod = (prod & pmask) ? ((prod << 1) ^ pp) : (prod << 1); + if (val & amask) prod ^= ta; + amask >>= 1; + } + *d64 = prod; + d64++; + s64++; + } + } + gf_do_final_region_alignment(&rd); +} + +static +void +gf_w64_bytwo_b_nosse_multiply_region(gf_t *gf, void *src, void *dest, gf_val_64_t val, int bytes, int xor) +{ + uint64_t *s64, *d64, ta, tb, prod, bmask, pp; + gf_region_data rd; + gf_internal_t *h; + + if (val == 0) { gf_multby_zero(dest, bytes, xor); return; } + if (val == 1) { gf_multby_one(src, dest, bytes, xor); return; } + + gf_set_region_data(&rd, gf, src, dest, bytes, val, xor, 8); + gf_do_initial_region_alignment(&rd); + + h = (gf_internal_t *) gf->scratch; + + s64 = (uint64_t *) rd.s_start; + d64 = (uint64_t *) rd.d_start; + bmask = 0x80000000; + bmask <<= 32; + pp = h->prim_poly; + + if (xor) { + while (s64 < (uint64_t *) rd.s_top) { + prod = 0; + tb = val; + ta = *s64; + while (1) { + if (tb & 1) prod ^= ta; + tb >>= 1; + if (tb == 0) break; + ta = (ta & bmask) ? ((ta << 1) ^ pp) : (ta << 1); + } + *d64 ^= prod; + d64++; + s64++; + } + } else { + while (s64 < (uint64_t *) rd.s_top) { + prod = 0; + tb = val; + ta = *s64; + while (1) { + if (tb & 1) prod ^= ta; + tb >>= 1; + if (tb == 0) break; + ta = (ta & bmask) ? ((ta << 1) ^ pp) : (ta << 1); + } + *d64 = prod; + d64++; + s64++; + } + } + gf_do_final_region_alignment(&rd); +} + +#define SSE_AB2(pp, m1 ,m2, va, t1, t2) {\ + t1 = _mm_and_si128(_mm_slli_epi64(va, 1), m1); \ + t2 = _mm_and_si128(va, m2); \ + t2 = _mm_sub_epi64 (_mm_slli_epi64(t2, 1), _mm_srli_epi64(t2, (GF_FIELD_WIDTH-1))); \ + va = _mm_xor_si128(t1, _mm_and_si128(t2, pp)); } + +#define BYTWO_P_ONESTEP {\ + SSE_AB2(pp, m1 ,m2, prod, t1, t2); \ + t1 = _mm_and_si128(v, one); \ + t1 = _mm_sub_epi64(t1, one); \ + t1 = _mm_and_si128(t1, ta); \ + prod = _mm_xor_si128(prod, t1); \ + v = _mm_srli_epi64(v, 1); } + + +#ifdef INTEL_SSE2 +void gf_w64_bytwo_p_sse_multiply_region(gf_t *gf, void *src, void *dest, gf_val_64_t val, int bytes, int xor) +{ + int i; + uint8_t *s8, *d8; + uint64_t vrev, one64; + uint64_t amask; + __m128i pp, m1, m2, ta, prod, t1, t2, tp, one, v; + gf_region_data rd; + gf_internal_t *h; + + if (val == 0) { gf_multby_zero(dest, bytes, xor); return; } + if (val == 1) { gf_multby_one(src, dest, bytes, xor); return; } + + gf_set_region_data(&rd, gf, src, dest, bytes, val, xor, 16); + gf_do_initial_region_alignment(&rd); + + h = (gf_internal_t *) gf->scratch; + one64 = 1; + vrev = 0; + for (i = 0; i < 64; i++) { + vrev <<= 1; + if (!(val & (one64 << i))) vrev |= 1; + } + + s8 = (uint8_t *) rd.s_start; + d8 = (uint8_t *) rd.d_start; + + amask = -1; + amask ^= 1; + pp = _mm_set1_epi64x(h->prim_poly); + m1 = _mm_set1_epi64x(amask); + m2 = _mm_set1_epi64x(one64 << 63); + one = _mm_set1_epi64x(1); + + while (d8 < (uint8_t *) rd.d_top) { + prod = _mm_setzero_si128(); + v = _mm_set1_epi64x(vrev); + ta = _mm_load_si128((__m128i *) s8); + tp = (!xor) ? _mm_setzero_si128() : _mm_load_si128((__m128i *) d8); + BYTWO_P_ONESTEP; BYTWO_P_ONESTEP; BYTWO_P_ONESTEP; BYTWO_P_ONESTEP; + BYTWO_P_ONESTEP; BYTWO_P_ONESTEP; BYTWO_P_ONESTEP; BYTWO_P_ONESTEP; + BYTWO_P_ONESTEP; BYTWO_P_ONESTEP; BYTWO_P_ONESTEP; BYTWO_P_ONESTEP; + BYTWO_P_ONESTEP; BYTWO_P_ONESTEP; BYTWO_P_ONESTEP; BYTWO_P_ONESTEP; + BYTWO_P_ONESTEP; BYTWO_P_ONESTEP; BYTWO_P_ONESTEP; BYTWO_P_ONESTEP; + BYTWO_P_ONESTEP; BYTWO_P_ONESTEP; BYTWO_P_ONESTEP; BYTWO_P_ONESTEP; + BYTWO_P_ONESTEP; BYTWO_P_ONESTEP; BYTWO_P_ONESTEP; BYTWO_P_ONESTEP; + BYTWO_P_ONESTEP; BYTWO_P_ONESTEP; BYTWO_P_ONESTEP; BYTWO_P_ONESTEP; + BYTWO_P_ONESTEP; BYTWO_P_ONESTEP; BYTWO_P_ONESTEP; BYTWO_P_ONESTEP; + BYTWO_P_ONESTEP; BYTWO_P_ONESTEP; BYTWO_P_ONESTEP; BYTWO_P_ONESTEP; + BYTWO_P_ONESTEP; BYTWO_P_ONESTEP; BYTWO_P_ONESTEP; BYTWO_P_ONESTEP; + BYTWO_P_ONESTEP; BYTWO_P_ONESTEP; BYTWO_P_ONESTEP; BYTWO_P_ONESTEP; + BYTWO_P_ONESTEP; BYTWO_P_ONESTEP; BYTWO_P_ONESTEP; BYTWO_P_ONESTEP; + BYTWO_P_ONESTEP; BYTWO_P_ONESTEP; BYTWO_P_ONESTEP; BYTWO_P_ONESTEP; + BYTWO_P_ONESTEP; BYTWO_P_ONESTEP; BYTWO_P_ONESTEP; BYTWO_P_ONESTEP; + BYTWO_P_ONESTEP; BYTWO_P_ONESTEP; BYTWO_P_ONESTEP; BYTWO_P_ONESTEP; + _mm_store_si128((__m128i *) d8, _mm_xor_si128(prod, tp)); + d8 += 16; + s8 += 16; + } + gf_do_final_region_alignment(&rd); +} +#endif + +#ifdef INTEL_SSE2 +static +void +gf_w64_bytwo_b_sse_region_2_xor(gf_region_data *rd) +{ + uint64_t one64, amask; + uint8_t *d8, *s8; + __m128i pp, m1, m2, t1, t2, va, vb; + gf_internal_t *h; + + s8 = (uint8_t *) rd->s_start; + d8 = (uint8_t *) rd->d_start; + + h = (gf_internal_t *) rd->gf->scratch; + one64 = 1; + amask = -1; + amask ^= 1; + pp = _mm_set1_epi64x(h->prim_poly); + m1 = _mm_set1_epi64x(amask); + m2 = _mm_set1_epi64x(one64 << 63); + + while (d8 < (uint8_t *) rd->d_top) { + va = _mm_load_si128 ((__m128i *)(s8)); + SSE_AB2(pp, m1, m2, va, t1, t2); + vb = _mm_load_si128 ((__m128i *)(d8)); + vb = _mm_xor_si128(vb, va); + _mm_store_si128((__m128i *)d8, vb); + d8 += 16; + s8 += 16; + } +} +#endif + +#ifdef INTEL_SSE2 +static +void +gf_w64_bytwo_b_sse_region_2_noxor(gf_region_data *rd) +{ + uint64_t one64, amask; + uint8_t *d8, *s8; + __m128i pp, m1, m2, t1, t2, va; + gf_internal_t *h; + + s8 = (uint8_t *) rd->s_start; + d8 = (uint8_t *) rd->d_start; + + h = (gf_internal_t *) rd->gf->scratch; + one64 = 1; + amask = -1; + amask ^= 1; + pp = _mm_set1_epi64x(h->prim_poly); + m1 = _mm_set1_epi64x(amask); + m2 = _mm_set1_epi64x(one64 << 63); + + while (d8 < (uint8_t *) rd->d_top) { + va = _mm_load_si128 ((__m128i *)(s8)); + SSE_AB2(pp, m1, m2, va, t1, t2); + _mm_store_si128((__m128i *)d8, va); + d8 += 16; + s8 += 16; + } +} +#endif + +#ifdef INTEL_SSE2 +static +void +gf_w64_bytwo_b_sse_multiply_region(gf_t *gf, void *src, void *dest, gf_val_64_t val, int bytes, int xor) +{ + uint64_t itb, amask, one64; + uint8_t *d8, *s8; + __m128i pp, m1, m2, t1, t2, va, vb; + gf_region_data rd; + gf_internal_t *h; + + if (val == 0) { gf_multby_zero(dest, bytes, xor); return; } + if (val == 1) { gf_multby_one(src, dest, bytes, xor); return; } + + gf_set_region_data(&rd, gf, src, dest, bytes, val, xor, 16); + gf_do_initial_region_alignment(&rd); + + if (val == 2) { + if (xor) { + gf_w64_bytwo_b_sse_region_2_xor(&rd); + } else { + gf_w64_bytwo_b_sse_region_2_noxor(&rd); + } + gf_do_final_region_alignment(&rd); + return; + } + + s8 = (uint8_t *) rd.s_start; + d8 = (uint8_t *) rd.d_start; + h = (gf_internal_t *) gf->scratch; + + one64 = 1; + amask = -1; + amask ^= 1; + pp = _mm_set1_epi64x(h->prim_poly); + m1 = _mm_set1_epi64x(amask); + m2 = _mm_set1_epi64x(one64 << 63); + + while (d8 < (uint8_t *) rd.d_top) { + va = _mm_load_si128 ((__m128i *)(s8)); + vb = (!xor) ? _mm_setzero_si128() : _mm_load_si128 ((__m128i *)(d8)); + itb = val; + while (1) { + if (itb & 1) vb = _mm_xor_si128(vb, va); + itb >>= 1; + if (itb == 0) break; + SSE_AB2(pp, m1, m2, va, t1, t2); + } + _mm_store_si128((__m128i *)d8, vb); + d8 += 16; + s8 += 16; + } + + gf_do_final_region_alignment(&rd); +} +#endif + + +static +int gf_w64_bytwo_init(gf_t *gf) +{ + gf_internal_t *h; + + h = (gf_internal_t *) gf->scratch; + + if (h->mult_type == GF_MULT_BYTWO_p) { + SET_FUNCTION(gf,multiply,w64,gf_w64_bytwo_p_multiply) + #ifdef INTEL_SSE2 + if (gf_cpu_supports_intel_sse2 && !(h->region_type & GF_REGION_NOSIMD)) { + SET_FUNCTION(gf,multiply_region,w64,gf_w64_bytwo_p_sse_multiply_region) + } else { + #endif + SET_FUNCTION(gf,multiply_region,w64,gf_w64_bytwo_p_nosse_multiply_region) + if(h->region_type & GF_REGION_SIMD) + return 0; + #ifdef INTEL_SSE2 + } + #endif + } else { + SET_FUNCTION(gf,multiply,w64,gf_w64_bytwo_b_multiply) + #ifdef INTEL_SSE2 + if (gf_cpu_supports_intel_sse2 && !(h->region_type & GF_REGION_NOSIMD)) { + SET_FUNCTION(gf,multiply_region,w64,gf_w64_bytwo_b_sse_multiply_region) + } else { + #endif + SET_FUNCTION(gf,multiply_region,w64,gf_w64_bytwo_b_nosse_multiply_region) + if(h->region_type & GF_REGION_SIMD) + return 0; + #ifdef INTEL_SSE2 + } + #endif + } + SET_FUNCTION(gf,inverse,w64,gf_w64_euclid) + return 1; +} + + +static +gf_val_64_t +gf_w64_composite_multiply(gf_t *gf, gf_val_64_t a, gf_val_64_t b) +{ + gf_internal_t *h = (gf_internal_t *) gf->scratch; + gf_t *base_gf = h->base_gf; + uint32_t b0 = b & 0x00000000ffffffff; + uint32_t b1 = (b & 0xffffffff00000000) >> 32; + uint32_t a0 = a & 0x00000000ffffffff; + uint32_t a1 = (a & 0xffffffff00000000) >> 32; + uint32_t a1b1; + + a1b1 = base_gf->multiply.w32(base_gf, a1, b1); + + return ((uint64_t)(base_gf->multiply.w32(base_gf, a0, b0) ^ a1b1) | + ((uint64_t)(base_gf->multiply.w32(base_gf, a1, b0) ^ base_gf->multiply.w32(base_gf, a0, b1) ^ base_gf->multiply.w32(base_gf, a1b1, h->prim_poly)) << 32)); +} + +/* + * Composite field division trick (explained in 2007 tech report) + * + * Compute a / b = a*b^-1, where p(x) = x^2 + sx + 1 + * + * let c = b^-1 + * + * c*b = (s*b1c1+b1c0+b0c1)x+(b1c1+b0c0) + * + * want (s*b1c1+b1c0+b0c1) = 0 and (b1c1+b0c0) = 1 + * + * let d = b1c1 and d+1 = b0c0 + * + * solve s*b1c1+b1c0+b0c1 = 0 + * + * solution: d = (b1b0^-1)(b1b0^-1+b0b1^-1+s)^-1 + * + * c0 = (d+1)b0^-1 + * c1 = d*b1^-1 + * + * a / b = a * c + */ + +static +gf_val_64_t +gf_w64_composite_inverse(gf_t *gf, gf_val_64_t a) +{ + gf_internal_t *h = (gf_internal_t *) gf->scratch; + gf_t *base_gf = h->base_gf; + uint32_t a0 = a & 0x00000000ffffffff; + uint32_t a1 = (a & 0xffffffff00000000) >> 32; + uint32_t c0, c1, d, tmp; + uint64_t c; + uint32_t a0inv, a1inv; + + if (a0 == 0) { + a1inv = base_gf->inverse.w32(base_gf, a1); + c0 = base_gf->multiply.w32(base_gf, a1inv, h->prim_poly); + c1 = a1inv; + } else if (a1 == 0) { + c0 = base_gf->inverse.w32(base_gf, a0); + c1 = 0; + } else { + a1inv = base_gf->inverse.w32(base_gf, a1); + a0inv = base_gf->inverse.w32(base_gf, a0); + + d = base_gf->multiply.w32(base_gf, a1, a0inv); + + tmp = (base_gf->multiply.w32(base_gf, a1, a0inv) ^ base_gf->multiply.w32(base_gf, a0, a1inv) ^ h->prim_poly); + tmp = base_gf->inverse.w32(base_gf, tmp); + + d = base_gf->multiply.w32(base_gf, d, tmp); + + c0 = base_gf->multiply.w32(base_gf, (d^1), a0inv); + c1 = base_gf->multiply.w32(base_gf, d, a1inv); + } + + c = c0 | ((uint64_t)c1 << 32); + + return c; +} + +static +void +gf_w64_composite_multiply_region(gf_t *gf, void *src, void *dest, gf_val_64_t val, int bytes, int xor) +{ + gf_internal_t *h = (gf_internal_t *) gf->scratch; + gf_t *base_gf = h->base_gf; + uint32_t b0 = val & 0x00000000ffffffff; + uint32_t b1 = (val & 0xffffffff00000000) >> 32; + uint64_t *s64, *d64; + uint64_t *top; + uint64_t a0, a1, a1b1; + gf_region_data rd; + + if (val == 0) { gf_multby_zero(dest, bytes, xor); return; } + gf_set_region_data(&rd, gf, src, dest, bytes, val, xor, 8); + + s64 = rd.s_start; + d64 = rd.d_start; + top = rd.d_top; + + if (xor) { + while (d64 < top) { + a0 = *s64 & 0x00000000ffffffff; + a1 = (*s64 & 0xffffffff00000000) >> 32; + a1b1 = base_gf->multiply.w32(base_gf, a1, b1); + + *d64 ^= ((uint64_t)(base_gf->multiply.w32(base_gf, a0, b0) ^ a1b1) | + ((uint64_t)(base_gf->multiply.w32(base_gf, a1, b0) ^ base_gf->multiply.w32(base_gf, a0, b1) ^ base_gf->multiply.w32(base_gf, a1b1, h->prim_poly)) << 32)); + s64++; + d64++; + } + } else { + while (d64 < top) { + a0 = *s64 & 0x00000000ffffffff; + a1 = (*s64 & 0xffffffff00000000) >> 32; + a1b1 = base_gf->multiply.w32(base_gf, a1, b1); + + *d64 = ((base_gf->multiply.w32(base_gf, a0, b0) ^ a1b1) | + ((uint64_t)(base_gf->multiply.w32(base_gf, a1, b0) ^ base_gf->multiply.w32(base_gf, a0, b1) ^ base_gf->multiply.w32(base_gf, a1b1, h->prim_poly)) << 32)); + s64++; + d64++; + } + } +} + +static +void +gf_w64_composite_multiply_region_alt(gf_t *gf, void *src, void *dest, gf_val_64_t val, int bytes, int xor) +{ + gf_internal_t *h = (gf_internal_t *) gf->scratch; + gf_t *base_gf = h->base_gf; + gf_val_32_t val0 = val & 0x00000000ffffffff; + gf_val_32_t val1 = (val & 0xffffffff00000000) >> 32; + uint8_t *slow, *shigh; + uint8_t *dlow, *dhigh, *top; + int sub_reg_size; + gf_region_data rd; + + if (!xor) { + memset(dest, 0, bytes); + } + + gf_set_region_data(&rd, gf, src, dest, bytes, val, xor, 32); + gf_do_initial_region_alignment(&rd); + + slow = (uint8_t *) rd.s_start; + dlow = (uint8_t *) rd.d_start; + top = (uint8_t*) rd.d_top; + sub_reg_size = (top - dlow)/2; + shigh = slow + sub_reg_size; + dhigh = dlow + sub_reg_size; + + base_gf->multiply_region.w32(base_gf, slow, dlow, val0, sub_reg_size, xor); + base_gf->multiply_region.w32(base_gf, shigh, dlow, val1, sub_reg_size, 1); + base_gf->multiply_region.w32(base_gf, slow, dhigh, val1, sub_reg_size, xor); + base_gf->multiply_region.w32(base_gf, shigh, dhigh, val0, sub_reg_size, 1); + base_gf->multiply_region.w32(base_gf, shigh, dhigh, base_gf->multiply.w32(base_gf, h->prim_poly, val1), sub_reg_size, 1); + + gf_do_final_region_alignment(&rd); +} + + + +static +int gf_w64_composite_init(gf_t *gf) +{ + gf_internal_t *h = (gf_internal_t *) gf->scratch; + + if (h->region_type & GF_REGION_ALTMAP) { + SET_FUNCTION(gf,multiply_region,w64,gf_w64_composite_multiply_region_alt) + } else { + SET_FUNCTION(gf,multiply_region,w64,gf_w64_composite_multiply_region) + } + + SET_FUNCTION(gf,multiply,w64,gf_w64_composite_multiply) + SET_FUNCTION(gf,divide,w64,NULL) + SET_FUNCTION(gf,inverse,w64,gf_w64_composite_inverse) + + return 1; +} + +#ifdef INTEL_SSSE3 +static + void +gf_w64_split_4_64_lazy_sse_altmap_multiply_region(gf_t *gf, void *src, void *dest, uint64_t val, int bytes, int xor) +{ + gf_internal_t *h; + int i, j, k; + uint64_t pp, v, *s64, *d64, *top; + __m128i si, tables[16][8], p[8], v0, mask1; + struct gf_split_4_64_lazy_data *ld; + uint8_t btable[16]; + gf_region_data rd; + + if (val == 0) { gf_multby_zero(dest, bytes, xor); return; } + if (val == 1) { gf_multby_one(src, dest, bytes, xor); return; } + + h = (gf_internal_t *) gf->scratch; + pp = h->prim_poly; + + gf_set_region_data(&rd, gf, src, dest, bytes, val, xor, 128); + gf_do_initial_region_alignment(&rd); + + s64 = (uint64_t *) rd.s_start; + d64 = (uint64_t *) rd.d_start; + top = (uint64_t *) rd.d_top; + + ld = (struct gf_split_4_64_lazy_data *) h->private; + + v = val; + for (i = 0; i < 16; i++) { + ld->tables[i][0] = 0; + for (j = 1; j < 16; j <<= 1) { + for (k = 0; k < j; k++) { + ld->tables[i][k^j] = (v ^ ld->tables[i][k]); + } + v = (v & GF_FIRST_BIT) ? ((v << 1) ^ pp) : (v << 1); + } + for (j = 0; j < 8; j++) { + for (k = 0; k < 16; k++) { + btable[k] = (uint8_t) ld->tables[i][k]; + ld->tables[i][k] >>= 8; + } + tables[i][j] = _mm_loadu_si128((__m128i *) btable); + } + } + + mask1 = _mm_set1_epi8(0xf); + + while (d64 != top) { + + if (xor) { + for (i = 0; i < 8; i++) p[i] = _mm_load_si128 ((__m128i *) (d64+i*2)); + } else { + for (i = 0; i < 8; i++) p[i] = _mm_setzero_si128(); + } + i = 0; + for (k = 0; k < 8; k++) { + v0 = _mm_load_si128((__m128i *) s64); + /* MM_PRINT8("v", v0); */ + s64 += 2; + + si = _mm_and_si128(v0, mask1); + + for (j = 0; j < 8; j++) { + p[j] = _mm_xor_si128(p[j], _mm_shuffle_epi8(tables[i][j], si)); + } + i++; + v0 = _mm_srli_epi32(v0, 4); + si = _mm_and_si128(v0, mask1); + for (j = 0; j < 8; j++) { + p[j] = _mm_xor_si128(p[j], _mm_shuffle_epi8(tables[i][j], si)); + } + i++; + } + for (i = 0; i < 8; i++) { + /* MM_PRINT8("v", p[i]); */ + _mm_store_si128((__m128i *) d64, p[i]); + d64 += 2; + } + } + gf_do_final_region_alignment(&rd); +} +#endif + +#ifdef INTEL_SSE4 +static + void +gf_w64_split_4_64_lazy_sse_multiply_region(gf_t *gf, void *src, void *dest, uint64_t val, int bytes, int xor) +{ + gf_internal_t *h; + int i, j, k; + uint64_t pp, v, *s64, *d64, *top; + __m128i si, tables[16][8], p[8], st[8], mask1, mask8, mask16, t1; + struct gf_split_4_64_lazy_data *ld; + uint8_t btable[16]; + gf_region_data rd; + + if (val == 0) { gf_multby_zero(dest, bytes, xor); return; } + if (val == 1) { gf_multby_one(src, dest, bytes, xor); return; } + + h = (gf_internal_t *) gf->scratch; + pp = h->prim_poly; + + gf_set_region_data(&rd, gf, src, dest, bytes, val, xor, 128); + gf_do_initial_region_alignment(&rd); + + s64 = (uint64_t *) rd.s_start; + d64 = (uint64_t *) rd.d_start; + top = (uint64_t *) rd.d_top; + + ld = (struct gf_split_4_64_lazy_data *) h->private; + + v = val; + for (i = 0; i < 16; i++) { + ld->tables[i][0] = 0; + for (j = 1; j < 16; j <<= 1) { + for (k = 0; k < j; k++) { + ld->tables[i][k^j] = (v ^ ld->tables[i][k]); + } + v = (v & GF_FIRST_BIT) ? ((v << 1) ^ pp) : (v << 1); + } + for (j = 0; j < 8; j++) { + for (k = 0; k < 16; k++) { + btable[k] = (uint8_t) ld->tables[i][k]; + ld->tables[i][k] >>= 8; + } + tables[i][j] = _mm_loadu_si128((__m128i *) btable); + } + } + + mask1 = _mm_set1_epi8(0xf); + mask8 = _mm_set1_epi16(0xff); + mask16 = _mm_set1_epi32(0xffff); + + while (d64 != top) { + + for (i = 0; i < 8; i++) p[i] = _mm_setzero_si128(); + + for (k = 0; k < 8; k++) { + st[k] = _mm_load_si128((__m128i *) s64); + s64 += 2; + } + + for (k = 0; k < 4; k ++) { + st[k] = _mm_shuffle_epi32(st[k], _MM_SHUFFLE(3,1,2,0)); + st[k+4] = _mm_shuffle_epi32(st[k+4], _MM_SHUFFLE(2,0,3,1)); + t1 = _mm_blend_epi16(st[k], st[k+4], 0xf0); + st[k] = _mm_srli_si128(st[k], 8); + st[k+4] = _mm_slli_si128(st[k+4], 8); + st[k+4] = _mm_blend_epi16(st[k], st[k+4], 0xf0); + st[k] = t1; + } + +/* + printf("After pack pass 1\n"); + for (k = 0; k < 8; k++) { + MM_PRINT8("v", st[k]); + } + printf("\n"); + */ + + t1 = _mm_packus_epi32(_mm_and_si128(st[0], mask16), _mm_and_si128(st[2], mask16)); + st[2] = _mm_packus_epi32(_mm_srli_epi32(st[0], 16), _mm_srli_epi32(st[2], 16)); + st[0] = t1; + t1 = _mm_packus_epi32(_mm_and_si128(st[1], mask16), _mm_and_si128(st[3], mask16)); + st[3] = _mm_packus_epi32(_mm_srli_epi32(st[1], 16), _mm_srli_epi32(st[3], 16)); + st[1] = t1; + t1 = _mm_packus_epi32(_mm_and_si128(st[4], mask16), _mm_and_si128(st[6], mask16)); + st[6] = _mm_packus_epi32(_mm_srli_epi32(st[4], 16), _mm_srli_epi32(st[6], 16)); + st[4] = t1; + t1 = _mm_packus_epi32(_mm_and_si128(st[5], mask16), _mm_and_si128(st[7], mask16)); + st[7] = _mm_packus_epi32(_mm_srli_epi32(st[5], 16), _mm_srli_epi32(st[7], 16)); + st[5] = t1; + +/* + printf("After pack pass 2\n"); + for (k = 0; k < 8; k++) { + MM_PRINT8("v", st[k]); + } + printf("\n"); + */ + t1 = _mm_packus_epi16(_mm_and_si128(st[0], mask8), _mm_and_si128(st[1], mask8)); + st[1] = _mm_packus_epi16(_mm_srli_epi16(st[0], 8), _mm_srli_epi16(st[1], 8)); + st[0] = t1; + t1 = _mm_packus_epi16(_mm_and_si128(st[2], mask8), _mm_and_si128(st[3], mask8)); + st[3] = _mm_packus_epi16(_mm_srli_epi16(st[2], 8), _mm_srli_epi16(st[3], 8)); + st[2] = t1; + t1 = _mm_packus_epi16(_mm_and_si128(st[4], mask8), _mm_and_si128(st[5], mask8)); + st[5] = _mm_packus_epi16(_mm_srli_epi16(st[4], 8), _mm_srli_epi16(st[5], 8)); + st[4] = t1; + t1 = _mm_packus_epi16(_mm_and_si128(st[6], mask8), _mm_and_si128(st[7], mask8)); + st[7] = _mm_packus_epi16(_mm_srli_epi16(st[6], 8), _mm_srli_epi16(st[7], 8)); + st[6] = t1; + +/* + printf("After final pack pass 2\n"); + for (k = 0; k < 8; k++) { + MM_PRINT8("v", st[k]); + } + */ + i = 0; + for (k = 0; k < 8; k++) { + si = _mm_and_si128(st[k], mask1); + + for (j = 0; j < 8; j++) { + p[j] = _mm_xor_si128(p[j], _mm_shuffle_epi8(tables[i][j], si)); + } + i++; + st[k] = _mm_srli_epi32(st[k], 4); + si = _mm_and_si128(st[k], mask1); + for (j = 0; j < 8; j++) { + p[j] = _mm_xor_si128(p[j], _mm_shuffle_epi8(tables[i][j], si)); + } + i++; + } + + t1 = _mm_unpacklo_epi8(p[0], p[1]); + p[1] = _mm_unpackhi_epi8(p[0], p[1]); + p[0] = t1; + t1 = _mm_unpacklo_epi8(p[2], p[3]); + p[3] = _mm_unpackhi_epi8(p[2], p[3]); + p[2] = t1; + t1 = _mm_unpacklo_epi8(p[4], p[5]); + p[5] = _mm_unpackhi_epi8(p[4], p[5]); + p[4] = t1; + t1 = _mm_unpacklo_epi8(p[6], p[7]); + p[7] = _mm_unpackhi_epi8(p[6], p[7]); + p[6] = t1; + +/* + printf("After unpack pass 1:\n"); + for (i = 0; i < 8; i++) { + MM_PRINT8("v", p[i]); + } + */ + + t1 = _mm_unpacklo_epi16(p[0], p[2]); + p[2] = _mm_unpackhi_epi16(p[0], p[2]); + p[0] = t1; + t1 = _mm_unpacklo_epi16(p[1], p[3]); + p[3] = _mm_unpackhi_epi16(p[1], p[3]); + p[1] = t1; + t1 = _mm_unpacklo_epi16(p[4], p[6]); + p[6] = _mm_unpackhi_epi16(p[4], p[6]); + p[4] = t1; + t1 = _mm_unpacklo_epi16(p[5], p[7]); + p[7] = _mm_unpackhi_epi16(p[5], p[7]); + p[5] = t1; + +/* + printf("After unpack pass 2:\n"); + for (i = 0; i < 8; i++) { + MM_PRINT8("v", p[i]); + } + */ + + t1 = _mm_unpacklo_epi32(p[0], p[4]); + p[4] = _mm_unpackhi_epi32(p[0], p[4]); + p[0] = t1; + t1 = _mm_unpacklo_epi32(p[1], p[5]); + p[5] = _mm_unpackhi_epi32(p[1], p[5]); + p[1] = t1; + t1 = _mm_unpacklo_epi32(p[2], p[6]); + p[6] = _mm_unpackhi_epi32(p[2], p[6]); + p[2] = t1; + t1 = _mm_unpacklo_epi32(p[3], p[7]); + p[7] = _mm_unpackhi_epi32(p[3], p[7]); + p[3] = t1; + + if (xor) { + for (i = 0; i < 8; i++) { + t1 = _mm_load_si128((__m128i *) d64); + _mm_store_si128((__m128i *) d64, _mm_xor_si128(p[i], t1)); + d64 += 2; + } + } else { + for (i = 0; i < 8; i++) { + _mm_store_si128((__m128i *) d64, p[i]); + d64 += 2; + } + } + + } + + gf_do_final_region_alignment(&rd); +} +#endif + +#define GF_MULTBY_TWO(p) (((p) & GF_FIRST_BIT) ? (((p) << 1) ^ h->prim_poly) : (p) << 1); + +static +int gf_w64_split_init(gf_t *gf) +{ + gf_internal_t *h; + struct gf_split_4_64_lazy_data *d4; + struct gf_split_8_64_lazy_data *d8; + struct gf_split_8_8_data *d88; + struct gf_split_16_64_lazy_data *d16; + uint64_t p, basep; + int exp, i, j; + + h = (gf_internal_t *) gf->scratch; + + /* Defaults */ + + SET_FUNCTION(gf,multiply_region,w64,gf_w64_multiply_region_from_single) + + SET_FUNCTION(gf,multiply,w64,gf_w64_bytwo_p_multiply) + +#if defined(INTEL_SSE4_PCLMUL) + if (gf_cpu_supports_intel_pclmul) { + if ((!(h->region_type & GF_REGION_NOSIMD) && + (h->arg1 == 64 || h->arg2 == 64)) || + h->mult_type == GF_MULT_DEFAULT){ + + if ((0xfffffffe00000000ULL & h->prim_poly) == 0){ + SET_FUNCTION(gf,multiply,w64,gf_w64_clm_multiply_2) + SET_FUNCTION(gf,multiply_region,w64,gf_w64_clm_multiply_region_from_single_2) + }else if((0xfffe000000000000ULL & h->prim_poly) == 0){ + SET_FUNCTION(gf,multiply,w64,gf_w64_clm_multiply_4) + SET_FUNCTION(gf,multiply_region,w64,gf_w64_clm_multiply_region_from_single_4) + }else{ + return 0; + } + } + } +#endif + + SET_FUNCTION(gf,inverse,w64,gf_w64_euclid) + + /* Allen: set region pointers for default mult type. Single pointers are + * taken care of above (explicitly for sse, implicitly for no sse). */ + + if (h->mult_type == GF_MULT_DEFAULT) { +#if defined(INTEL_SSE4) || defined(ARCH_AARCH64) + if (gf_cpu_supports_intel_sse4 || gf_cpu_supports_arm_neon) { + d4 = (struct gf_split_4_64_lazy_data *) h->private; + d4->last_value = 0; +#if defined(INTEL_SSE4) + if (gf_cpu_supports_intel_sse4) + SET_FUNCTION(gf,multiply_region,w64,gf_w64_split_4_64_lazy_sse_multiply_region) +#elif defined(ARCH_AARCH64) + if (gf_cpu_supports_arm_neon) + gf_w64_neon_split_init(gf); +#endif + } else { +#endif + d8 = (struct gf_split_8_64_lazy_data *) h->private; + d8->last_value = 0; + SET_FUNCTION(gf,multiply_region,w64,gf_w64_split_8_64_lazy_multiply_region) +#if defined(INTEL_SSE4) || defined(ARCH_AARCH64) + } +#endif + } + + if ((h->arg1 == 4 && h->arg2 == 64) || (h->arg1 == 64 && h->arg2 == 4)) { + d4 = (struct gf_split_4_64_lazy_data *) h->private; + d4->last_value = 0; + + if((h->region_type & GF_REGION_ALTMAP) && (h->region_type & GF_REGION_NOSIMD)) return 0; + if(h->region_type & GF_REGION_ALTMAP) + { + #ifdef INTEL_SSSE3 + if (gf_cpu_supports_intel_ssse3) { + SET_FUNCTION(gf,multiply_region,w64,gf_w64_split_4_64_lazy_sse_altmap_multiply_region) + } else + #elif defined(ARCH_AARCH64) + if (gf_cpu_supports_arm_neon) { + gf_w64_neon_split_init(gf); + } else + #endif + return 0; + } + else //no altmap + { + #if defined(INTEL_SSE4) || defined(ARCH_AARCH64) + if(gf_cpu_supports_intel_sse4 || gf_cpu_supports_arm_neon) { + if (h->region_type & GF_REGION_NOSIMD) { + SET_FUNCTION(gf,multiply_region,w64,gf_w64_split_4_64_lazy_multiply_region) + } else + #if defined(INTEL_SSE4) + SET_FUNCTION(gf,multiply_region,w64,gf_w64_split_4_64_lazy_sse_multiply_region) + #elif defined(ARCH_AARCH64) + gf_w64_neon_split_init(gf); + #endif + } else { + #endif + SET_FUNCTION(gf,multiply_region,w64,gf_w64_split_4_64_lazy_multiply_region) + if(h->region_type & GF_REGION_SIMD) + return 0; + #if defined(INTEL_SSE4) || defined(ARCH_AARCH64) + } + #endif + } + } + if ((h->arg1 == 8 && h->arg2 == 64) || (h->arg1 == 64 && h->arg2 == 8)) { + d8 = (struct gf_split_8_64_lazy_data *) h->private; + d8->last_value = 0; + SET_FUNCTION(gf,multiply_region,w64,gf_w64_split_8_64_lazy_multiply_region) + } + if ((h->arg1 == 16 && h->arg2 == 64) || (h->arg1 == 64 && h->arg2 == 16)) { + d16 = (struct gf_split_16_64_lazy_data *) h->private; + d16->last_value = 0; + SET_FUNCTION(gf,multiply_region,w64,gf_w64_split_16_64_lazy_multiply_region) + } + if ((h->arg1 == 8 && h->arg2 == 8)) { + d88 = (struct gf_split_8_8_data *) h->private; + SET_FUNCTION(gf,multiply,w64,gf_w64_split_8_8_multiply) + + /* The performance of this guy sucks, so don't bother with a region op */ + + basep = 1; + for (exp = 0; exp < 15; exp++) { + for (j = 0; j < 256; j++) d88->tables[exp][0][j] = 0; + for (i = 0; i < 256; i++) d88->tables[exp][i][0] = 0; + d88->tables[exp][1][1] = basep; + for (i = 2; i < 256; i++) { + if (i&1) { + p = d88->tables[exp][i^1][1]; + d88->tables[exp][i][1] = p ^ basep; + } else { + p = d88->tables[exp][i>>1][1]; + d88->tables[exp][i][1] = GF_MULTBY_TWO(p); + } + } + for (i = 1; i < 256; i++) { + p = d88->tables[exp][i][1]; + for (j = 1; j < 256; j++) { + if (j&1) { + d88->tables[exp][i][j] = d88->tables[exp][i][j^1] ^ p; + } else { + d88->tables[exp][i][j] = GF_MULTBY_TWO(d88->tables[exp][i][j>>1]); + } + } + } + for (i = 0; i < 8; i++) basep = GF_MULTBY_TWO(basep); + } + } + return 1; +} + +int gf_w64_scratch_size(int mult_type, int region_type, int divide_type, int arg1, int arg2) +{ + switch(mult_type) + { + case GF_MULT_SHIFT: + return sizeof(gf_internal_t); + break; + case GF_MULT_CARRY_FREE: + return sizeof(gf_internal_t); + break; + case GF_MULT_BYTWO_p: + case GF_MULT_BYTWO_b: + return sizeof(gf_internal_t); + break; + + case GF_MULT_DEFAULT: + + /* Allen: set the *local* arg1 and arg2, just for scratch size purposes, + * then fall through to split table scratch size code. */ + +#if defined(INTEL_SSE4) || defined(ARCH_AARCH64) + if (gf_cpu_supports_intel_sse4 || gf_cpu_supports_arm_neon) { + arg1 = 64; + arg2 = 4; + } else { +#endif + arg1 = 64; + arg2 = 8; +#if defined(INTEL_SSE4) || defined(ARCH_AARCH64) + } +#endif + + case GF_MULT_SPLIT_TABLE: + if (arg1 == 8 && arg2 == 8) { + return sizeof(gf_internal_t) + sizeof(struct gf_split_8_8_data) + 64; + } + if ((arg1 == 16 && arg2 == 64) || (arg2 == 16 && arg1 == 64)) { + return sizeof(gf_internal_t) + sizeof(struct gf_split_16_64_lazy_data) + 64; + } + if ((arg1 == 8 && arg2 == 64) || (arg2 == 8 && arg1 == 64)) { + return sizeof(gf_internal_t) + sizeof(struct gf_split_8_64_lazy_data) + 64; + } + + if ((arg1 == 64 && arg2 == 4) || (arg1 == 4 && arg2 == 64)) { + return sizeof(gf_internal_t) + sizeof(struct gf_split_4_64_lazy_data) + 64; + } + return 0; + case GF_MULT_GROUP: + return sizeof(gf_internal_t) + sizeof(struct gf_w64_group_data) + + sizeof(uint64_t) * (1 << arg1) + + sizeof(uint64_t) * (1 << arg2) + 64; + break; + case GF_MULT_COMPOSITE: + if (arg1 == 2) return sizeof(gf_internal_t) + 64; + return 0; + break; + default: + return 0; + } +} + +int gf_w64_init(gf_t *gf) +{ + gf_internal_t *h; + + h = (gf_internal_t *) gf->scratch; + + /* Allen: set default primitive polynomial / irreducible polynomial if needed */ + + /* Omitting the leftmost 1 as in w=32 */ + + if (h->prim_poly == 0) { + if (h->mult_type == GF_MULT_COMPOSITE) { + h->prim_poly = gf_composite_get_default_poly(h->base_gf); + if (h->prim_poly == 0) return 0; /* This shouldn't happen */ + } else { + h->prim_poly = 0x1b; + } + } + + SET_FUNCTION(gf,multiply,w64,NULL) + SET_FUNCTION(gf,divide,w64,NULL) + SET_FUNCTION(gf,inverse,w64,NULL) + SET_FUNCTION(gf,multiply_region,w64,NULL) + + switch(h->mult_type) { + case GF_MULT_CARRY_FREE: if (gf_w64_cfm_init(gf) == 0) return 0; break; + case GF_MULT_SHIFT: if (gf_w64_shift_init(gf) == 0) return 0; break; + case GF_MULT_COMPOSITE: if (gf_w64_composite_init(gf) == 0) return 0; break; + case GF_MULT_DEFAULT: + case GF_MULT_SPLIT_TABLE: if (gf_w64_split_init(gf) == 0) return 0; break; + case GF_MULT_GROUP: if (gf_w64_group_init(gf) == 0) return 0; break; + case GF_MULT_BYTWO_p: + case GF_MULT_BYTWO_b: if (gf_w64_bytwo_init(gf) == 0) return 0; break; + default: return 0; + } + if (h->divide_type == GF_DIVIDE_EUCLID) { + SET_FUNCTION(gf,divide,w64,gf_w64_divide_from_inverse) + SET_FUNCTION(gf,inverse,w64,gf_w64_euclid) + } + + if (gf->inverse.w64 != NULL && gf->divide.w64 == NULL) { + SET_FUNCTION(gf,divide,w64,gf_w64_divide_from_inverse) + } + if (gf->inverse.w64 == NULL && gf->divide.w64 != NULL) { + SET_FUNCTION(gf,inverse,w64,gf_w64_inverse_from_divide) + } + + if (h->region_type == GF_REGION_CAUCHY) return 0; + + if (h->region_type & GF_REGION_ALTMAP) { + if (h->mult_type == GF_MULT_COMPOSITE) { + SET_FUNCTION(gf,extract_word,w64,gf_w64_composite_extract_word) + } else if (h->mult_type == GF_MULT_SPLIT_TABLE) { + SET_FUNCTION(gf,extract_word,w64,gf_w64_split_extract_word) + } + } else { + SET_FUNCTION(gf,extract_word,w64,gf_w64_extract_word) + } + + return 1; +} diff --git a/IDA_new/gf-complete/src/gf_w8.c b/IDA_new/gf-complete/src/gf_w8.c new file mode 100644 index 0000000..f647a31 --- /dev/null +++ b/IDA_new/gf-complete/src/gf_w8.c @@ -0,0 +1,2398 @@ +/* + * GF-Complete: A Comprehensive Open Source Library for Galois Field Arithmetic + * James S. Plank, Ethan L. Miller, Kevin M. Greenan, + * Benjamin A. Arnold, John A. Burnum, Adam W. Disney, Allen C. McBride. + * + * gf_w8.c + * + * Routines for 8-bit Galois fields + */ + +#include "gf_int.h" +#include "gf_w8.h" +#include +#include +#include +#include "gf_cpu.h" + +#define AB2(ip, am1 ,am2, b, t1, t2) {\ + t1 = (b << 1) & am1;\ + t2 = b & am2; \ + t2 = ((t2 << 1) - (t2 >> (GF_FIELD_WIDTH-1))); \ + b = (t1 ^ (t2 & ip));} + +#define SSE_AB2(pp, m1 ,m2, va, t1, t2) {\ + t1 = _mm_and_si128(_mm_slli_epi64(va, 1), m1); \ + t2 = _mm_and_si128(va, m2); \ + t2 = _mm_sub_epi64 (_mm_slli_epi64(t2, 1), _mm_srli_epi64(t2, (GF_FIELD_WIDTH-1))); \ + va = _mm_xor_si128(t1, _mm_and_si128(t2, pp)); } + +#define MM_PRINT(s, r) { uint8_t blah[16], ii; printf("%-12s", s); _mm_storeu_si128((__m128i *)blah, r); for (ii = 0; ii < 16; ii += 2) printf(" %02x %02x", blah[15-ii], blah[14-ii]); printf("\n"); } + +static +inline +uint32_t gf_w8_inverse_from_divide (gf_t *gf, uint32_t a) +{ + return gf->divide.w32(gf, 1, a); +} + +static +inline +uint32_t gf_w8_divide_from_inverse (gf_t *gf, uint32_t a, uint32_t b) +{ + b = gf->inverse.w32(gf, b); + return gf->multiply.w32(gf, a, b); +} + +static +inline +uint32_t gf_w8_euclid (gf_t *gf, uint32_t b) +{ + uint32_t e_i, e_im1, e_ip1; + uint32_t d_i, d_im1, d_ip1; + uint32_t y_i, y_im1, y_ip1; + uint32_t c_i; + + if (b == 0) return -1; + e_im1 = ((gf_internal_t *) (gf->scratch))->prim_poly; + e_i = b; + d_im1 = 8; + for (d_i = d_im1; ((1 << d_i) & e_i) == 0; d_i--) ; + y_i = 1; + y_im1 = 0; + + while (e_i != 1) { + + e_ip1 = e_im1; + d_ip1 = d_im1; + c_i = 0; + + while (d_ip1 >= d_i) { + c_i ^= (1 << (d_ip1 - d_i)); + e_ip1 ^= (e_i << (d_ip1 - d_i)); + if (e_ip1 == 0) return 0; + while ((e_ip1 & (1 << d_ip1)) == 0) d_ip1--; + } + + y_ip1 = y_im1 ^ gf->multiply.w32(gf, c_i, y_i); + y_im1 = y_i; + y_i = y_ip1; + + e_im1 = e_i; + d_im1 = d_i; + e_i = e_ip1; + d_i = d_ip1; + } + + return y_i; +} + +static +gf_val_32_t gf_w8_extract_word(gf_t *gf, void *start, int bytes, int index) +{ + uint8_t *r8; + + r8 = (uint8_t *) start; + return r8[index]; +} + +static +gf_val_32_t gf_w8_composite_extract_word(gf_t *gf, void *start, int bytes, int index) +{ + int sub_size; + gf_internal_t *h; + uint8_t *r8, *top; + uint8_t a, b; + gf_region_data rd; + + h = (gf_internal_t *) gf->scratch; + gf_set_region_data(&rd, gf, start, start, bytes, 0, 0, 32); + r8 = (uint8_t *) start; + if (r8 + index < (uint8_t *) rd.d_start) return r8[index]; + if (r8 + index >= (uint8_t *) rd.d_top) return r8[index]; + index -= (((uint8_t *) rd.d_start) - r8); + r8 = (uint8_t *) rd.d_start; + top = (uint8_t *) rd.d_top; + sub_size = (top-r8)/2; + + a = h->base_gf->extract_word.w32(h->base_gf, r8, sub_size, index); + b = h->base_gf->extract_word.w32(h->base_gf, r8+sub_size, sub_size, index); + return (a | (b << 4)); +} + +static +inline +uint32_t gf_w8_matrix (gf_t *gf, uint32_t b) +{ + return gf_bitmatrix_inverse(b, 8, ((gf_internal_t *) (gf->scratch))->prim_poly); +} + + +#if defined(INTEL_SSE4_PCLMUL) +static +inline +gf_val_32_t +gf_w8_clm_multiply_2 (gf_t *gf, gf_val_32_t a8, gf_val_32_t b8) +{ + gf_val_32_t rv = 0; + + __m128i a, b; + __m128i result; + __m128i prim_poly; + __m128i w; + gf_internal_t * h = gf->scratch; + + a = _mm_insert_epi32 (_mm_setzero_si128(), a8, 0); + b = _mm_insert_epi32 (a, b8, 0); + + prim_poly = _mm_set_epi32(0, 0, 0, (uint32_t)(h->prim_poly & 0x1ffULL)); + + /* Do the initial multiply */ + + result = _mm_clmulepi64_si128 (a, b, 0); + + /* Ben: Do prim_poly reduction twice. We are guaranteed that we will only + have to do the reduction at most twice, because (w-2)/z == 2. Where + z is equal to the number of zeros after the leading 1 + + _mm_clmulepi64_si128 is the carryless multiply operation. Here + _mm_srli_si128 shifts the result to the right by 1 byte. This allows + us to multiply the prim_poly by the leading bits of the result. We + then xor the result of that operation back with the result.*/ + + w = _mm_clmulepi64_si128 (prim_poly, _mm_srli_si128 (result, 1), 0); + result = _mm_xor_si128 (result, w); + w = _mm_clmulepi64_si128 (prim_poly, _mm_srli_si128 (result, 1), 0); + result = _mm_xor_si128 (result, w); + + /* Extracts 32 bit value from result. */ + + rv = ((gf_val_32_t)_mm_extract_epi32(result, 0)); + + return rv; +} +#endif + +#if defined(INTEL_SSE4_PCLMUL) +static +inline +gf_val_32_t +gf_w8_clm_multiply_3 (gf_t *gf, gf_val_32_t a8, gf_val_32_t b8) +{ + gf_val_32_t rv = 0; + + __m128i a, b; + __m128i result; + __m128i prim_poly; + __m128i w; + gf_internal_t * h = gf->scratch; + + a = _mm_insert_epi32 (_mm_setzero_si128(), a8, 0); + b = _mm_insert_epi32 (a, b8, 0); + + prim_poly = _mm_set_epi32(0, 0, 0, (uint32_t)(h->prim_poly & 0x1ffULL)); + + /* Do the initial multiply */ + + result = _mm_clmulepi64_si128 (a, b, 0); + + w = _mm_clmulepi64_si128 (prim_poly, _mm_srli_si128 (result, 1), 0); + result = _mm_xor_si128 (result, w); + w = _mm_clmulepi64_si128 (prim_poly, _mm_srli_si128 (result, 1), 0); + result = _mm_xor_si128 (result, w); + w = _mm_clmulepi64_si128 (prim_poly, _mm_srli_si128 (result, 1), 0); + result = _mm_xor_si128 (result, w); + + /* Extracts 32 bit value from result. */ + + rv = ((gf_val_32_t)_mm_extract_epi32(result, 0)); + + return rv; +} +#endif + +#if defined(INTEL_SSE4_PCLMUL) +static +inline +gf_val_32_t +gf_w8_clm_multiply_4 (gf_t *gf, gf_val_32_t a8, gf_val_32_t b8) +{ + gf_val_32_t rv = 0; + + __m128i a, b; + __m128i result; + __m128i prim_poly; + __m128i w; + gf_internal_t * h = gf->scratch; + + a = _mm_insert_epi32 (_mm_setzero_si128(), a8, 0); + b = _mm_insert_epi32 (a, b8, 0); + + prim_poly = _mm_set_epi32(0, 0, 0, (uint32_t)(h->prim_poly & 0x1ffULL)); + + /* Do the initial multiply */ + + result = _mm_clmulepi64_si128 (a, b, 0); + + w = _mm_clmulepi64_si128 (prim_poly, _mm_srli_si128 (result, 1), 0); + result = _mm_xor_si128 (result, w); + w = _mm_clmulepi64_si128 (prim_poly, _mm_srli_si128 (result, 1), 0); + result = _mm_xor_si128 (result, w); + w = _mm_clmulepi64_si128 (prim_poly, _mm_srli_si128 (result, 1), 0); + result = _mm_xor_si128 (result, w); + w = _mm_clmulepi64_si128 (prim_poly, _mm_srli_si128 (result, 1), 0); + result = _mm_xor_si128 (result, w); + + /* Extracts 32 bit value from result. */ + rv = ((gf_val_32_t)_mm_extract_epi32(result, 0)); + + return rv; +} +#endif + + +static +void +gf_w8_multiply_region_from_single(gf_t *gf, void *src, void *dest, gf_val_32_t val, int bytes, int + xor) +{ + gf_region_data rd; + uint8_t *s8; + uint8_t *d8; + + if (val == 0) { gf_multby_zero(dest, bytes, xor); return; } + if (val == 1) { gf_multby_one(src, dest, bytes, xor); return; } + + gf_set_region_data(&rd, gf, src, dest, bytes, val, xor, 1); + gf_do_initial_region_alignment(&rd); + + s8 = (uint8_t *) rd.s_start; + d8 = (uint8_t *) rd.d_start; + + if (xor) { + while (d8 < ((uint8_t *) rd.d_top)) { + *d8 ^= gf->multiply.w32(gf, val, *s8); + d8++; + s8++; + } + } else { + while (d8 < ((uint8_t *) rd.d_top)) { + *d8 = gf->multiply.w32(gf, val, *s8); + d8++; + s8++; + } + } + gf_do_final_region_alignment(&rd); +} + +#if defined(INTEL_SSE4_PCLMUL) +static +void +gf_w8_clm_multiply_region_from_single_2(gf_t *gf, void *src, void *dest, gf_val_32_t val, int bytes, int + xor) +{ + gf_region_data rd; + uint8_t *s8; + uint8_t *d8; + + __m128i a, b; + __m128i result; + __m128i prim_poly; + __m128i w; + gf_internal_t * h = gf->scratch; + + prim_poly = _mm_set_epi32(0, 0, 0, (uint32_t)(h->prim_poly & 0x1ffULL)); + + if (val == 0) { gf_multby_zero(dest, bytes, xor); return; } + if (val == 1) { gf_multby_one(src, dest, bytes, xor); return; } + + a = _mm_insert_epi32 (_mm_setzero_si128(), val, 0); + + gf_set_region_data(&rd, gf, src, dest, bytes, val, xor, 1); + gf_do_initial_region_alignment(&rd); + + s8 = (uint8_t *) rd.s_start; + d8 = (uint8_t *) rd.d_start; + + if (xor) { + while (d8 < ((uint8_t *) rd.d_top)) { + b = _mm_insert_epi32 (a, (gf_val_32_t)(*s8), 0); + result = _mm_clmulepi64_si128 (a, b, 0); + w = _mm_clmulepi64_si128 (prim_poly, _mm_srli_si128 (result, 1), 0); + result = _mm_xor_si128 (result, w); + w = _mm_clmulepi64_si128 (prim_poly, _mm_srli_si128 (result, 1), 0); + result = _mm_xor_si128 (result, w); + *d8 ^= ((gf_val_32_t)_mm_extract_epi32(result, 0)); + d8++; + s8++; + } + } else { + while (d8 < ((uint8_t *) rd.d_top)) { + b = _mm_insert_epi32 (a, (gf_val_32_t)(*s8), 0); + result = _mm_clmulepi64_si128 (a, b, 0); + w = _mm_clmulepi64_si128 (prim_poly, _mm_srli_si128 (result, 1), 0); + result = _mm_xor_si128 (result, w); + w = _mm_clmulepi64_si128 (prim_poly, _mm_srli_si128 (result, 1), 0); + result = _mm_xor_si128 (result, w); + *d8 = ((gf_val_32_t)_mm_extract_epi32(result, 0)); + d8++; + s8++; + } + } + gf_do_final_region_alignment(&rd); +} +#endif + +#if defined(INTEL_SSE4_PCLMUL) +static +void +gf_w8_clm_multiply_region_from_single_3(gf_t *gf, void *src, void *dest, gf_val_32_t val, int bytes, int + xor) +{ + gf_region_data rd; + uint8_t *s8; + uint8_t *d8; + + __m128i a, b; + __m128i result; + __m128i prim_poly; + __m128i w; + gf_internal_t * h = gf->scratch; + + prim_poly = _mm_set_epi32(0, 0, 0, (uint32_t)(h->prim_poly & 0x1ffULL)); + + if (val == 0) { gf_multby_zero(dest, bytes, xor); return; } + if (val == 1) { gf_multby_one(src, dest, bytes, xor); return; } + + a = _mm_insert_epi32 (_mm_setzero_si128(), val, 0); + + gf_set_region_data(&rd, gf, src, dest, bytes, val, xor, 1); + gf_do_initial_region_alignment(&rd); + + s8 = (uint8_t *) rd.s_start; + d8 = (uint8_t *) rd.d_start; + + if (xor) { + while (d8 < ((uint8_t *) rd.d_top)) { + b = _mm_insert_epi32 (a, (gf_val_32_t)(*s8), 0); + result = _mm_clmulepi64_si128 (a, b, 0); + w = _mm_clmulepi64_si128 (prim_poly, _mm_srli_si128 (result, 1), 0); + result = _mm_xor_si128 (result, w); + w = _mm_clmulepi64_si128 (prim_poly, _mm_srli_si128 (result, 1), 0); + result = _mm_xor_si128 (result, w); + w = _mm_clmulepi64_si128 (prim_poly, _mm_srli_si128 (result, 1), 0); + result = _mm_xor_si128 (result, w); + *d8 ^= ((gf_val_32_t)_mm_extract_epi32(result, 0)); + d8++; + s8++; + } + } else { + while (d8 < ((uint8_t *) rd.d_top)) { + b = _mm_insert_epi32 (a, (gf_val_32_t)(*s8), 0); + result = _mm_clmulepi64_si128 (a, b, 0); + w = _mm_clmulepi64_si128 (prim_poly, _mm_srli_si128 (result, 1), 0); + result = _mm_xor_si128 (result, w); + w = _mm_clmulepi64_si128 (prim_poly, _mm_srli_si128 (result, 1), 0); + result = _mm_xor_si128 (result, w); + w = _mm_clmulepi64_si128 (prim_poly, _mm_srli_si128 (result, 1), 0); + result = _mm_xor_si128 (result, w); + *d8 = ((gf_val_32_t)_mm_extract_epi32(result, 0)); + d8++; + s8++; + } + } + gf_do_final_region_alignment(&rd); +} +#endif + +#if defined(INTEL_SSE4_PCLMUL) +static +void +gf_w8_clm_multiply_region_from_single_4(gf_t *gf, void *src, void *dest, gf_val_32_t val, int bytes, int + xor) +{ + gf_region_data rd; + uint8_t *s8; + uint8_t *d8; + + __m128i a, b; + __m128i result; + __m128i prim_poly; + __m128i w; + gf_internal_t * h = gf->scratch; + + prim_poly = _mm_set_epi32(0, 0, 0, (uint32_t)(h->prim_poly & 0x1ffULL)); + + if (val == 0) { gf_multby_zero(dest, bytes, xor); return; } + if (val == 1) { gf_multby_one(src, dest, bytes, xor); return; } + + a = _mm_insert_epi32 (_mm_setzero_si128(), val, 0); + + gf_set_region_data(&rd, gf, src, dest, bytes, val, xor, 1); + gf_do_initial_region_alignment(&rd); + + s8 = (uint8_t *) rd.s_start; + d8 = (uint8_t *) rd.d_start; + + if (xor) { + while (d8 < ((uint8_t *) rd.d_top)) { + b = _mm_insert_epi32 (a, (gf_val_32_t)(*s8), 0); + result = _mm_clmulepi64_si128 (a, b, 0); + w = _mm_clmulepi64_si128 (prim_poly, _mm_srli_si128 (result, 1), 0); + result = _mm_xor_si128 (result, w); + w = _mm_clmulepi64_si128 (prim_poly, _mm_srli_si128 (result, 1), 0); + result = _mm_xor_si128 (result, w); + w = _mm_clmulepi64_si128 (prim_poly, _mm_srli_si128 (result, 1), 0); + result = _mm_xor_si128 (result, w); + w = _mm_clmulepi64_si128 (prim_poly, _mm_srli_si128 (result, 1), 0); + result = _mm_xor_si128 (result, w); + *d8 ^= ((gf_val_32_t)_mm_extract_epi32(result, 0)); + d8++; + s8++; + } + } else { + while (d8 < ((uint8_t *) rd.d_top)) { + b = _mm_insert_epi32 (a, (gf_val_32_t)(*s8), 0); + result = _mm_clmulepi64_si128 (a, b, 0); + w = _mm_clmulepi64_si128 (prim_poly, _mm_srli_si128 (result, 1), 0); + result = _mm_xor_si128 (result, w); + w = _mm_clmulepi64_si128 (prim_poly, _mm_srli_si128 (result, 1), 0); + result = _mm_xor_si128 (result, w); + w = _mm_clmulepi64_si128 (prim_poly, _mm_srli_si128 (result, 1), 0); + result = _mm_xor_si128 (result, w); + w = _mm_clmulepi64_si128 (prim_poly, _mm_srli_si128 (result, 1), 0); + result = _mm_xor_si128 (result, w); + *d8 = ((gf_val_32_t)_mm_extract_epi32(result, 0)); + d8++; + s8++; + } + } + gf_do_final_region_alignment(&rd); +} +#endif + +/* ------------------------------------------------------------ +IMPLEMENTATION: SHIFT: + +JSP: The world's dumbest multiplication algorithm. I only +include it for completeness. It does have the feature that it requires no +extra memory. + */ + +static +inline + uint32_t +gf_w8_shift_multiply (gf_t *gf, uint32_t a8, uint32_t b8) +{ + uint16_t product, i, pp, a, b; + gf_internal_t *h; + + a = a8; + b = b8; + h = (gf_internal_t *) gf->scratch; + pp = h->prim_poly; + + product = 0; + + for (i = 0; i < GF_FIELD_WIDTH; i++) { + if (a & (1 << i)) product ^= (b << i); + } + for (i = (GF_FIELD_WIDTH*2-2); i >= GF_FIELD_WIDTH; i--) { + if (product & (1 << i)) product ^= (pp << (i-GF_FIELD_WIDTH)); + } + return product; +} + +static +int gf_w8_cfm_init(gf_t *gf) +{ +#if defined(INTEL_SSE4_PCLMUL) + if (gf_cpu_supports_intel_pclmul) { + gf_internal_t *h; + + h = (gf_internal_t *) gf->scratch; + + if ((0xe0 & h->prim_poly) == 0){ + SET_FUNCTION(gf,multiply,w32,gf_w8_clm_multiply_2) + SET_FUNCTION(gf,multiply_region,w32,gf_w8_clm_multiply_region_from_single_2) + }else if ((0xc0 & h->prim_poly) == 0){ + SET_FUNCTION(gf,multiply,w32,gf_w8_clm_multiply_3) + SET_FUNCTION(gf,multiply_region,w32,gf_w8_clm_multiply_region_from_single_3) + }else if ((0x80 & h->prim_poly) == 0){ + SET_FUNCTION(gf,multiply,w32,gf_w8_clm_multiply_4) + SET_FUNCTION(gf,multiply_region,w32,gf_w8_clm_multiply_region_from_single_4) + }else{ + return 0; + } + return 1; + } +#elif defined(ARM_NEON) + if (gf_cpu_supports_arm_neon) { + return gf_w8_neon_cfm_init(gf); + } +#endif + + return 0; + +} + +static +int gf_w8_shift_init(gf_t *gf) +{ + SET_FUNCTION(gf,multiply,w32,gf_w8_shift_multiply) /* The others will be set automatically */ + return 1; +} + +/* ------------------------------------------------------------ +IMPLEMENTATION: LOG_TABLE: + +JSP: Kevin wrote this, and I'm converting it to my structure. +*/ + +static +inline + uint32_t +gf_w8_logzero_multiply (gf_t *gf, uint32_t a, uint32_t b) +{ + struct gf_w8_logzero_table_data *ltd; + + ltd = (struct gf_w8_logzero_table_data *) ((gf_internal_t *) gf->scratch)->private; + return ltd->antilog_tbl[ltd->log_tbl[a] + ltd->log_tbl[b]]; +} + +static +inline + uint32_t +gf_w8_logzero_divide (gf_t *gf, uint32_t a, uint32_t b) +{ + struct gf_w8_logzero_table_data *ltd; + + ltd = (struct gf_w8_logzero_table_data *) ((gf_internal_t *) gf->scratch)->private; + return ltd->div_tbl[ltd->log_tbl[a] - ltd->log_tbl[b]]; +} + +static +inline + uint32_t +gf_w8_logzero_small_multiply (gf_t *gf, uint32_t a, uint32_t b) +{ + struct gf_w8_logzero_small_table_data *std; + + std = (struct gf_w8_logzero_small_table_data *) ((gf_internal_t *) gf->scratch)->private; + if (b == 0) return 0; + return std->antilog_tbl[std->log_tbl[a] + std->log_tbl[b]]; +} + +static +inline + uint32_t +gf_w8_logzero_small_divide (gf_t *gf, uint32_t a, uint32_t b) +{ + struct gf_w8_logzero_small_table_data *std; + + std = (struct gf_w8_logzero_small_table_data *) ((gf_internal_t *) gf->scratch)->private; + return std->div_tbl[std->log_tbl[a] - std->log_tbl[b]]; +} + +static +inline + uint32_t +gf_w8_log_multiply (gf_t *gf, uint32_t a, uint32_t b) +{ + struct gf_w8_logtable_data *ltd; + + ltd = (struct gf_w8_logtable_data *) ((gf_internal_t *) gf->scratch)->private; + return (a == 0 || b == 0) ? 0 : ltd->antilog_tbl[(unsigned)(ltd->log_tbl[a] + ltd->log_tbl[b])]; +} + +static +inline + uint32_t +gf_w8_log_divide (gf_t *gf, uint32_t a, uint32_t b) +{ + int log_sum = 0; + struct gf_w8_logtable_data *ltd; + + if (a == 0 || b == 0) return 0; + ltd = (struct gf_w8_logtable_data *) ((gf_internal_t *) gf->scratch)->private; + + log_sum = ltd->log_tbl[a] - ltd->log_tbl[b] + (GF_MULT_GROUP_SIZE); + return (ltd->antilog_tbl[log_sum]); +} + +static + uint32_t +gf_w8_log_inverse (gf_t *gf, uint32_t a) +{ + struct gf_w8_logtable_data *ltd; + + ltd = (struct gf_w8_logtable_data *) ((gf_internal_t *) gf->scratch)->private; + return (ltd->inv_tbl[a]); +} + +static + uint32_t +gf_w8_logzero_inverse (gf_t *gf, uint32_t a) +{ + struct gf_w8_logzero_table_data *ltd; + + ltd = (struct gf_w8_logzero_table_data *) ((gf_internal_t *) gf->scratch)->private; + return (ltd->inv_tbl[a]); +} + +static + uint32_t +gf_w8_logzero_small_inverse (gf_t *gf, uint32_t a) +{ + struct gf_w8_logzero_small_table_data *std; + + std = (struct gf_w8_logzero_small_table_data *) ((gf_internal_t *) gf->scratch)->private; + return (std->inv_tbl[a]); +} + +static + void +gf_w8_log_multiply_region(gf_t *gf, void *src, void *dest, uint32_t val, int bytes, int xor) +{ + int i; + uint8_t lv; + uint8_t *s8, *d8; + struct gf_w8_logtable_data *ltd; + + if (val == 0) { gf_multby_zero(dest, bytes, xor); return; } + if (val == 1) { gf_multby_one(src, dest, bytes, xor); return; } + + ltd = (struct gf_w8_logtable_data *) ((gf_internal_t *) gf->scratch)->private; + s8 = (uint8_t *) src; + d8 = (uint8_t *) dest; + + lv = ltd->log_tbl[val]; + + if (xor) { + for (i = 0; i < bytes; i++) { + d8[i] ^= (s8[i] == 0 ? 0 : ltd->antilog_tbl[lv + ltd->log_tbl[s8[i]]]); + } + } else { + for (i = 0; i < bytes; i++) { + d8[i] = (s8[i] == 0 ? 0 : ltd->antilog_tbl[lv + ltd->log_tbl[s8[i]]]); + } + } +} + +static + void +gf_w8_logzero_multiply_region(gf_t *gf, void *src, void *dest, uint32_t val, int bytes, int xor) +{ + int i; + uint8_t lv; + uint8_t *s8, *d8; + struct gf_w8_logzero_table_data *ltd; + struct gf_w8_logzero_small_table_data *std; + short *log; + uint8_t *alt; + gf_internal_t *h; + + if (val == 0) { gf_multby_zero(dest, bytes, xor); return; } + if (val == 1) { gf_multby_one(src, dest, bytes, xor); return; } + + h = (gf_internal_t *) gf->scratch; + + if (h->arg1 == 1) { + std = (struct gf_w8_logzero_small_table_data *) h->private; + log = std->log_tbl; + alt = std->antilog_tbl; + } else { + ltd = (struct gf_w8_logzero_table_data *) h->private; + log = ltd->log_tbl; + alt = ltd->antilog_tbl; + } + s8 = (uint8_t *) src; + d8 = (uint8_t *) dest; + + lv = log[val]; + + if (xor) { + for (i = 0; i < bytes; i++) { + d8[i] ^= (alt[lv + log[s8[i]]]); + } + } else { + for (i = 0; i < bytes; i++) { + d8[i] = (alt[lv + log[s8[i]]]); + } + } +} + + static +int gf_w8_log_init(gf_t *gf) +{ + gf_internal_t *h; + struct gf_w8_logtable_data *ltd = NULL; + struct gf_w8_logzero_table_data *ztd = NULL; + struct gf_w8_logzero_small_table_data *std = NULL; + uint8_t *alt; + uint8_t *inv; + int i, b; + int check = 0; + + h = (gf_internal_t *) gf->scratch; + if (h->mult_type == GF_MULT_LOG_TABLE) { + ltd = h->private; + alt = ltd->antilog_tbl; + inv = ltd->inv_tbl; + } else if (h->mult_type == GF_MULT_LOG_ZERO) { + std = h->private; + alt = std->antilog_tbl; + std->div_tbl = (alt + 255); + inv = std->inv_tbl; + } else { + ztd = h->private; + alt = ztd->antilog_tbl; + ztd->inv_tbl = (alt + 512 + 256); + ztd->div_tbl = (alt + 255); + inv = ztd->inv_tbl; + } + + for (i = 0; i < GF_MULT_GROUP_SIZE+1; i++) { + if (h->mult_type == GF_MULT_LOG_TABLE) + ltd->log_tbl[i] = 0; + else if (h->mult_type == GF_MULT_LOG_ZERO) + std->log_tbl[i] = 0; + else + ztd->log_tbl[i] = 0; + } + + if (h->mult_type == GF_MULT_LOG_TABLE) { + ltd->log_tbl[0] = 0; + } else if (h->mult_type == GF_MULT_LOG_ZERO) { + std->log_tbl[0] = 510; + } else { + ztd->log_tbl[0] = 512; + } + + b = 1; + for (i = 0; i < GF_MULT_GROUP_SIZE; i++) { + if (h->mult_type == GF_MULT_LOG_TABLE) { + if (ltd->log_tbl[b] != 0) check = 1; + ltd->log_tbl[b] = i; + } else if (h->mult_type == GF_MULT_LOG_ZERO) { + if (std->log_tbl[b] != 0) check = 1; + std->log_tbl[b] = i; + } else { + if (ztd->log_tbl[b] != 0) check = 1; + ztd->log_tbl[b] = i; + } + alt[i] = b; + alt[i+GF_MULT_GROUP_SIZE] = b; + b <<= 1; + if (b & GF_FIELD_SIZE) { + b = b ^ h->prim_poly; + } + } + if (check) { + _gf_errno = GF_E_LOGPOLY; + return 0; + } + + if (h->mult_type == GF_MULT_LOG_ZERO) bzero(alt+510, 255); + + if (h->mult_type == GF_MULT_LOG_ZERO_EXT) { + bzero(alt+512, 255); + alt[512+512] = 0; + } + + inv[0] = 0; /* Not really, but we need to fill it with something */ + i = 1; + b = GF_MULT_GROUP_SIZE; + do { + inv[i] = alt[b]; + i <<= 1; + if (i & (1 << 8)) i ^= h->prim_poly; + b--; + } while (i != 1); + + if (h->mult_type == GF_MULT_LOG_TABLE) { + SET_FUNCTION(gf,inverse,w32,gf_w8_log_inverse) + SET_FUNCTION(gf,divide,w32,gf_w8_log_divide) + SET_FUNCTION(gf,multiply,w32,gf_w8_log_multiply) + SET_FUNCTION(gf,multiply_region,w32,gf_w8_log_multiply_region) + } else if (h->mult_type == GF_MULT_LOG_ZERO) { + SET_FUNCTION(gf,inverse,w32,gf_w8_logzero_small_inverse) + SET_FUNCTION(gf,divide,w32,gf_w8_logzero_small_divide) + SET_FUNCTION(gf,multiply,w32,gf_w8_logzero_small_multiply) + SET_FUNCTION(gf,multiply_region,w32,gf_w8_logzero_multiply_region) + } else { + SET_FUNCTION(gf,inverse,w32,gf_w8_logzero_inverse) + SET_FUNCTION(gf,divide,w32,gf_w8_logzero_divide) + SET_FUNCTION(gf,multiply,w32,gf_w8_logzero_multiply) + SET_FUNCTION(gf,multiply_region,w32,gf_w8_logzero_multiply_region) + } + return 1; +} + +/* ------------------------------------------------------------ +IMPLEMENTATION: FULL_TABLE: + +JSP: Kevin wrote this, and I'm converting it to my structure. + */ + +static + gf_val_32_t +gf_w8_table_multiply(gf_t *gf, gf_val_32_t a, gf_val_32_t b) +{ + struct gf_w8_single_table_data *ftd; + + ftd = (struct gf_w8_single_table_data *) ((gf_internal_t *) gf->scratch)->private; + return (ftd->multtable[a][b]); +} + +static + gf_val_32_t +gf_w8_table_divide(gf_t *gf, gf_val_32_t a, gf_val_32_t b) +{ + struct gf_w8_single_table_data *ftd; + + ftd = (struct gf_w8_single_table_data *) ((gf_internal_t *) gf->scratch)->private; + return (ftd->divtable[a][b]); +} + +static + gf_val_32_t +gf_w8_default_multiply(gf_t *gf, gf_val_32_t a, gf_val_32_t b) +{ + struct gf_w8_default_data *ftd; + + ftd = (struct gf_w8_default_data *) ((gf_internal_t *) gf->scratch)->private; + return (ftd->multtable[a][b]); +} + +#if defined(INTEL_SSSE3) || defined(ARM_NEON) +static + gf_val_32_t +gf_w8_default_divide(gf_t *gf, gf_val_32_t a, gf_val_32_t b) +{ + struct gf_w8_default_data *ftd; + + ftd = (struct gf_w8_default_data *) ((gf_internal_t *) gf->scratch)->private; + return (ftd->divtable[a][b]); +} +#endif + +static + gf_val_32_t +gf_w8_double_table_multiply(gf_t *gf, gf_val_32_t a, gf_val_32_t b) +{ + struct gf_w8_double_table_data *ftd; + + ftd = (struct gf_w8_double_table_data *) ((gf_internal_t *) gf->scratch)->private; + return (ftd->mult[a][b]); +} + +static + gf_val_32_t +gf_w8_double_table_divide(gf_t *gf, gf_val_32_t a, gf_val_32_t b) +{ + struct gf_w8_double_table_data *ftd; + + ftd = (struct gf_w8_double_table_data *) ((gf_internal_t *) gf->scratch)->private; + return (ftd->div[a][b]); +} + +static + void +gf_w8_double_table_multiply_region(gf_t *gf, void *src, void *dest, gf_val_32_t val, int bytes, int xor) +{ + uint16_t *base; + uint32_t b, c, vc, vb; + gf_internal_t *h; + struct gf_w8_double_table_data *dtd; + struct gf_w8_double_table_lazy_data *ltd; + gf_region_data rd; + + if (val == 0) { gf_multby_zero(dest, bytes, xor); return; } + if (val == 1) { gf_multby_one(src, dest, bytes, xor); return; } + + h = (gf_internal_t *) (gf->scratch); + if (h->region_type & GF_REGION_LAZY) { + ltd = (struct gf_w8_double_table_lazy_data *) h->private; + base = ltd->mult; + for (b = 0; b < GF_FIELD_SIZE; b++) { + vb = (ltd->smult[val][b] << 8); + for (c = 0; c < GF_FIELD_SIZE; c++) { + vc = ltd->smult[val][c]; + base[(b << 8)| c] = (vb | vc); + } + } + + } else { + dtd = (struct gf_w8_double_table_data *) h->private; + base = &(dtd->mult[val][0]); + } + + gf_set_region_data(&rd, gf, src, dest, bytes, val, xor, 8); + gf_do_initial_region_alignment(&rd); + gf_two_byte_region_table_multiply(&rd, base); + gf_do_final_region_alignment(&rd); +} + +static + gf_val_32_t +gf_w8_double_table_lazy_multiply(gf_t *gf, gf_val_32_t a, gf_val_32_t b) +{ + struct gf_w8_double_table_lazy_data *ftd; + + ftd = (struct gf_w8_double_table_lazy_data *) ((gf_internal_t *) gf->scratch)->private; + return (ftd->smult[a][b]); +} + +static + gf_val_32_t +gf_w8_double_table_lazy_divide(gf_t *gf, gf_val_32_t a, gf_val_32_t b) +{ + struct gf_w8_double_table_lazy_data *ftd; + + ftd = (struct gf_w8_double_table_lazy_data *) ((gf_internal_t *) gf->scratch)->private; + return (ftd->div[a][b]); +} + +static + void +gf_w8_table_multiply_region(gf_t *gf, void *src, void *dest, gf_val_32_t val, int bytes, int xor) +{ + int i; + uint8_t *s8, *d8; + struct gf_w8_single_table_data *ftd; + + if (val == 0) { gf_multby_zero(dest, bytes, xor); return; } + if (val == 1) { gf_multby_one(src, dest, bytes, xor); return; } + + ftd = (struct gf_w8_single_table_data *) ((gf_internal_t *) gf->scratch)->private; + s8 = (uint8_t *) src; + d8 = (uint8_t *) dest; + + if (xor) { + for (i = 0; i < bytes; i++) { + d8[i] ^= ftd->multtable[s8[i]][val]; + } + } else { + for (i = 0; i < bytes; i++) { + d8[i] = ftd->multtable[s8[i]][val]; + } + } +} + +#ifdef INTEL_SSSE3 +static + void +gf_w8_split_multiply_region_sse(gf_t *gf, void *src, void *dest, gf_val_32_t val, int bytes, int xor) +{ + uint8_t *bh, *bl, *sptr, *dptr; + __m128i loset, t1, r, va, mth, mtl; + struct gf_w8_half_table_data *htd; + gf_region_data rd; + + if (val == 0) { gf_multby_zero(dest, bytes, xor); return; } + if (val == 1) { gf_multby_one(src, dest, bytes, xor); return; } + + htd = (struct gf_w8_half_table_data *) ((gf_internal_t *) (gf->scratch))->private; + + gf_set_region_data(&rd, gf, src, dest, bytes, val, xor, 16); + gf_do_initial_region_alignment(&rd); + + bh = (uint8_t *) htd->high; + bh += (val << 4); + bl = (uint8_t *) htd->low; + bl += (val << 4); + + sptr = rd.s_start; + dptr = rd.d_start; + + mth = _mm_loadu_si128 ((__m128i *)(bh)); + mtl = _mm_loadu_si128 ((__m128i *)(bl)); + loset = _mm_set1_epi8 (0x0f); + + if (xor) { + while (sptr < (uint8_t *) rd.s_top) { + va = _mm_load_si128 ((__m128i *)(sptr)); + t1 = _mm_and_si128 (loset, va); + r = _mm_shuffle_epi8 (mtl, t1); + va = _mm_srli_epi64 (va, 4); + t1 = _mm_and_si128 (loset, va); + r = _mm_xor_si128 (r, _mm_shuffle_epi8 (mth, t1)); + va = _mm_load_si128 ((__m128i *)(dptr)); + r = _mm_xor_si128 (r, va); + _mm_store_si128 ((__m128i *)(dptr), r); + dptr += 16; + sptr += 16; + } + } else { + while (sptr < (uint8_t *) rd.s_top) { + va = _mm_load_si128 ((__m128i *)(sptr)); + t1 = _mm_and_si128 (loset, va); + r = _mm_shuffle_epi8 (mtl, t1); + va = _mm_srli_epi64 (va, 4); + t1 = _mm_and_si128 (loset, va); + r = _mm_xor_si128 (r, _mm_shuffle_epi8 (mth, t1)); + _mm_store_si128 ((__m128i *)(dptr), r); + dptr += 16; + sptr += 16; + } + } + + gf_do_final_region_alignment(&rd); +} +#endif + + +/* ------------------------------------------------------------ +IMPLEMENTATION: FULL_TABLE: + */ + +static + gf_val_32_t +gf_w8_split_multiply(gf_t *gf, gf_val_32_t a, gf_val_32_t b) +{ + struct gf_w8_half_table_data *htd; + htd = (struct gf_w8_half_table_data *) ((gf_internal_t *) gf->scratch)->private; + + return htd->high[b][a>>4] ^ htd->low[b][a&0xf]; +} + +static + void +gf_w8_split_multiply_region(gf_t *gf, void *src, void *dest, gf_val_32_t val, int bytes, int xor) +{ + int i; + uint8_t *s8, *d8; + struct gf_w8_half_table_data *htd; + + if (val == 0) { gf_multby_zero(dest, bytes, xor); return; } + if (val == 1) { gf_multby_one(src, dest, bytes, xor); return; } + + htd = (struct gf_w8_half_table_data *) ((gf_internal_t *) gf->scratch)->private; + s8 = (uint8_t *) src; + d8 = (uint8_t *) dest; + + if (xor) { + for (i = 0; i < bytes; i++) { + d8[i] ^= (htd->high[val][s8[i]>>4] ^ htd->low[val][s8[i]&0xf]); + } + } else { + for (i = 0; i < bytes; i++) { + d8[i] = (htd->high[val][s8[i]>>4] ^ htd->low[val][s8[i]&0xf]); + } + } +} + + + static +int gf_w8_split_init(gf_t *gf) +{ + gf_internal_t *h; + struct gf_w8_half_table_data *htd; + int a, b; + + h = (gf_internal_t *) gf->scratch; + htd = (struct gf_w8_half_table_data *)h->private; + + bzero(htd->high, sizeof(uint8_t)*GF_FIELD_SIZE*GF_HALF_SIZE); + bzero(htd->low, sizeof(uint8_t)*GF_FIELD_SIZE*GF_HALF_SIZE); + + for (a = 1; a < GF_FIELD_SIZE; a++) { + for (b = 1; b < GF_HALF_SIZE; b++) { + htd->low[a][b] = gf_w8_shift_multiply(gf,a,b); + htd->high[a][b] = gf_w8_shift_multiply(gf,a,b<<4); + } + } + + SET_FUNCTION(gf,multiply,w32,gf_w8_split_multiply) + + #if defined(INTEL_SSSE3) + if (gf_cpu_supports_intel_ssse3 && !(h->region_type & GF_REGION_NOSIMD)) { + SET_FUNCTION(gf,multiply_region,w32,gf_w8_split_multiply_region_sse) + } else { + #elif defined(ARM_NEON) + if (gf_cpu_supports_arm_neon && !(h->region_type & GF_REGION_NOSIMD)) { + gf_w8_neon_split_init(gf); + } else { + #endif + SET_FUNCTION(gf,multiply_region,w32,gf_w8_split_multiply_region) + if(h->region_type & GF_REGION_SIMD) + return 0; + #if defined(INTEL_SSSE3) || defined(ARM_NEON) + } + #endif + + return 1; +} + +/* JSP: This is disgusting, but it is what it is. If there is no SSE, + then the default is equivalent to single table. If there is SSE, then + we use the "gf_w8_default_data" which is a hybrid of SPLIT & TABLE. */ + +static +int gf_w8_table_init(gf_t *gf) +{ + gf_internal_t *h; + struct gf_w8_single_table_data *ftd = NULL; + struct gf_w8_double_table_data *dtd = NULL; + struct gf_w8_double_table_lazy_data *ltd = NULL; + struct gf_w8_default_data *dd = NULL; + int a, b, c, prod, scase; + + h = (gf_internal_t *) gf->scratch; + + if (h->mult_type == GF_MULT_DEFAULT && + (gf_cpu_supports_intel_ssse3 || gf_cpu_supports_arm_neon)) { + dd = (struct gf_w8_default_data *)h->private; + scase = 3; + bzero(dd->high, sizeof(uint8_t) * GF_FIELD_SIZE * GF_HALF_SIZE); + bzero(dd->low, sizeof(uint8_t) * GF_FIELD_SIZE * GF_HALF_SIZE); + bzero(dd->divtable, sizeof(uint8_t) * GF_FIELD_SIZE * GF_FIELD_SIZE); + bzero(dd->multtable, sizeof(uint8_t) * GF_FIELD_SIZE * GF_FIELD_SIZE); + } else if (h->mult_type == GF_MULT_DEFAULT || + h->region_type == 0 || (h->region_type & GF_REGION_CAUCHY)) { + ftd = (struct gf_w8_single_table_data *)h->private; + bzero(ftd->divtable, sizeof(uint8_t) * GF_FIELD_SIZE * GF_FIELD_SIZE); + bzero(ftd->multtable, sizeof(uint8_t) * GF_FIELD_SIZE * GF_FIELD_SIZE); + scase = 0; + } else if (h->region_type == GF_REGION_DOUBLE_TABLE) { + dtd = (struct gf_w8_double_table_data *)h->private; + bzero(dtd->div, sizeof(uint8_t) * GF_FIELD_SIZE * GF_FIELD_SIZE); + bzero(dtd->mult, sizeof(uint16_t) * GF_FIELD_SIZE * GF_FIELD_SIZE * GF_FIELD_SIZE); + scase = 1; + } else if (h->region_type == (GF_REGION_DOUBLE_TABLE | GF_REGION_LAZY)) { + ltd = (struct gf_w8_double_table_lazy_data *)h->private; + bzero(ltd->div, sizeof(uint8_t) * GF_FIELD_SIZE * GF_FIELD_SIZE); + bzero(ltd->smult, sizeof(uint8_t) * GF_FIELD_SIZE * GF_FIELD_SIZE); + scase = 2; + } else { + fprintf(stderr, "Internal error in gf_w8_table_init\n"); + assert(0); + } + + for (a = 1; a < GF_FIELD_SIZE; a++) { + for (b = 1; b < GF_FIELD_SIZE; b++) { + prod = gf_w8_shift_multiply(gf,a,b); + switch (scase) { + case 0: + ftd->multtable[a][b] = prod; + ftd->divtable[prod][b] = a; + break; + case 1: + dtd->div[prod][b] = a; + for (c = 0; c < GF_FIELD_SIZE; c++) { + dtd->mult[a][(c<<8)|b] |= prod; + dtd->mult[a][(b<<8)|c] |= (prod<<8); + } + break; + case 2: + ltd->div[prod][b] = a; + ltd->smult[a][b] = prod; + break; + case 3: + dd->multtable[a][b] = prod; + dd->divtable[prod][b] = a; + if ((b & 0xf) == b) { dd->low[a][b] = prod; } + if ((b & 0xf0) == b) { dd->high[a][b>>4] = prod; } + break; + } + } + } + + SET_FUNCTION(gf,inverse,w32,NULL) /* Will set from divide */ + switch (scase) { + case 0: + SET_FUNCTION(gf,divide,w32,gf_w8_table_divide) + SET_FUNCTION(gf,multiply,w32,gf_w8_table_multiply) + SET_FUNCTION(gf,multiply_region,w32,gf_w8_table_multiply_region) + break; + case 1: + SET_FUNCTION(gf,divide,w32,gf_w8_double_table_divide) + SET_FUNCTION(gf,multiply,w32,gf_w8_double_table_multiply) + SET_FUNCTION(gf,multiply_region,w32,gf_w8_double_table_multiply_region) + break; + case 2: + SET_FUNCTION(gf,divide,w32,gf_w8_double_table_lazy_divide) + SET_FUNCTION(gf,multiply,w32,gf_w8_double_table_lazy_multiply) + SET_FUNCTION(gf,multiply_region,w32,gf_w8_double_table_multiply_region) + break; + case 3: +#if defined(INTEL_SSSE3) || defined(ARM_NEON) + if (gf_cpu_supports_intel_ssse3 || gf_cpu_supports_arm_neon) { + SET_FUNCTION(gf,divide,w32,gf_w8_default_divide) + SET_FUNCTION(gf,multiply,w32,gf_w8_default_multiply) +#if defined(INTEL_SSSE3) + if (gf_cpu_supports_intel_ssse3) { + SET_FUNCTION(gf,multiply_region,w32,gf_w8_split_multiply_region_sse) + } +#elif defined(ARM_NEON) + if (gf_cpu_supports_arm_neon) { + gf_w8_neon_split_init(gf); + } +#endif + } +#endif + break; + } + return 1; +} + +static + void +gf_w8_composite_multiply_region_alt(gf_t *gf, void *src, void *dest, gf_val_32_t val, int bytes, int xor) +{ + gf_internal_t *h = (gf_internal_t *) gf->scratch; + gf_t *base_gf = h->base_gf; + uint8_t val0 = val & 0x0f; + uint8_t val1 = (val & 0xf0) >> 4; + gf_region_data rd; + int sub_reg_size; + + if (val == 0) { + if (xor) return; + bzero(dest, bytes); + return; + } + + gf_set_region_data(&rd, gf, src, dest, bytes, val, xor, 32); + gf_do_initial_region_alignment(&rd); + + sub_reg_size = ((uint8_t *)rd.d_top - (uint8_t *)rd.d_start) / 2; + + base_gf->multiply_region.w32(base_gf, rd.s_start, rd.d_start, val0, sub_reg_size, xor); + base_gf->multiply_region.w32(base_gf, (uint8_t *)rd.s_start+sub_reg_size, rd.d_start, val1, sub_reg_size, 1); + base_gf->multiply_region.w32(base_gf, rd.s_start, (uint8_t *)rd.d_start+sub_reg_size, val1, sub_reg_size, xor); + base_gf->multiply_region.w32(base_gf, (uint8_t *)rd.s_start+sub_reg_size, (uint8_t *)rd.d_start+sub_reg_size, val0, sub_reg_size, 1); + base_gf->multiply_region.w32(base_gf, (uint8_t *)rd.s_start+sub_reg_size, (uint8_t *)rd.d_start+sub_reg_size, base_gf->multiply.w32(base_gf, h->prim_poly, val1), sub_reg_size, 1); + + gf_do_final_region_alignment(&rd); +} + +static +gf_val_32_t +gf_w8_composite_multiply_recursive(gf_t *gf, gf_val_32_t a, gf_val_32_t b) +{ + gf_internal_t *h = (gf_internal_t *) gf->scratch; + gf_t *base_gf = h->base_gf; + uint8_t b0 = b & 0x0f; + uint8_t b1 = (b & 0xf0) >> 4; + uint8_t a0 = a & 0x0f; + uint8_t a1 = (a & 0xf0) >> 4; + uint8_t a1b1; + + a1b1 = base_gf->multiply.w32(base_gf, a1, b1); + + return ((base_gf->multiply.w32(base_gf, a0, b0) ^ a1b1) | + ((base_gf->multiply.w32(base_gf, a1, b0) ^ + base_gf->multiply.w32(base_gf, a0, b1) ^ + base_gf->multiply.w32(base_gf, a1b1, h->prim_poly)) << 4)); +} + +static +gf_val_32_t +gf_w8_composite_multiply_inline(gf_t *gf, gf_val_32_t a, gf_val_32_t b) +{ + gf_internal_t *h = (gf_internal_t *) gf->scratch; + uint8_t b0 = b & 0x0f; + uint8_t b1 = (b & 0xf0) >> 4; + uint8_t a0 = a & 0x0f; + uint8_t a1 = (a & 0xf0) >> 4; + uint8_t a1b1, *mt; + struct gf_w8_composite_data *cd; + + cd = (struct gf_w8_composite_data *) h->private; + mt = cd->mult_table; + + a1b1 = GF_W4_INLINE_MULTDIV(mt, a1, b1); + + return ((GF_W4_INLINE_MULTDIV(mt, a0, b0) ^ a1b1) | + ((GF_W4_INLINE_MULTDIV(mt, a1, b0) ^ + GF_W4_INLINE_MULTDIV(mt, a0, b1) ^ + GF_W4_INLINE_MULTDIV(mt, a1b1, h->prim_poly)) << 4)); +} + +/* + * Composite field division trick (explained in 2007 tech report) + * + * Compute a / b = a*b^-1, where p(x) = x^2 + sx + 1 + * + * let c = b^-1 + * + * c*b = (s*b1c1+b1c0+b0c1)x+(b1c1+b0c0) + * + * want (s*b1c1+b1c0+b0c1) = 0 and (b1c1+b0c0) = 1 + * + * let d = b1c1 and d+1 = b0c0 + * + * solve s*b1c1+b1c0+b0c1 = 0 + * + * solution: d = (b1b0^-1)(b1b0^-1+b0b1^-1+s)^-1 + * + * c0 = (d+1)b0^-1 + * c1 = d*b1^-1 + * + * a / b = a * c + */ + +static +gf_val_32_t +gf_w8_composite_inverse(gf_t *gf, gf_val_32_t a) +{ + gf_internal_t *h = (gf_internal_t *) gf->scratch; + gf_t *base_gf = h->base_gf; + uint8_t a0 = a & 0x0f; + uint8_t a1 = (a & 0xf0) >> 4; + uint8_t c0, c1, c, d, tmp; + uint8_t a0inv, a1inv; + + if (a0 == 0) { + a1inv = base_gf->inverse.w32(base_gf, a1) & 0xf; + c0 = base_gf->multiply.w32(base_gf, a1inv, h->prim_poly); + c1 = a1inv; + } else if (a1 == 0) { + c0 = base_gf->inverse.w32(base_gf, a0); + c1 = 0; + } else { + a1inv = base_gf->inverse.w32(base_gf, a1) & 0xf; + a0inv = base_gf->inverse.w32(base_gf, a0) & 0xf; + + d = base_gf->multiply.w32(base_gf, a1, a0inv) & 0xf; + + tmp = (base_gf->multiply.w32(base_gf, a1, a0inv) ^ base_gf->multiply.w32(base_gf, a0, a1inv) ^ h->prim_poly) & 0xf; + tmp = base_gf->inverse.w32(base_gf, tmp) & 0xf; + + d = base_gf->multiply.w32(base_gf, d, tmp) & 0xf; + + c0 = base_gf->multiply.w32(base_gf, (d^1), a0inv) & 0xf; + c1 = base_gf->multiply.w32(base_gf, d, a1inv) & 0xf; + } + + c = c0 | (c1 << 4); + + return c; +} + +static +void +gf_w8_composite_multiply_region(gf_t *gf, void *src, void *dest, gf_val_32_t val, int bytes, int xor) +{ + gf_region_data rd; + gf_internal_t *h = (gf_internal_t *) gf->scratch; + gf_t *base_gf = h->base_gf; + uint8_t b0 = val & 0x0f; + uint8_t b1 = (val & 0xf0) >> 4; + uint8_t *s8; + uint8_t *d8; + uint8_t *mt; + uint8_t a0, a1, a1b1; + struct gf_w8_composite_data *cd; + + cd = (struct gf_w8_composite_data *) h->private; + + if (val == 0) { + if (xor) return; + bzero(dest, bytes); + return; + } + + gf_set_region_data(&rd, gf, src, dest, bytes, val, xor, 1); + gf_do_initial_region_alignment(&rd); + + + s8 = (uint8_t *) rd.s_start; + d8 = (uint8_t *) rd.d_start; + + mt = cd->mult_table; + if (mt == NULL) { + if (xor) { + while (d8 < (uint8_t *) rd.d_top) { + a0 = *s8 & 0x0f; + a1 = (*s8 & 0xf0) >> 4; + a1b1 = base_gf->multiply.w32(base_gf, a1, b1); + + *d8 ^= ((base_gf->multiply.w32(base_gf, a0, b0) ^ a1b1) | + ((base_gf->multiply.w32(base_gf, a1, b0) ^ + base_gf->multiply.w32(base_gf, a0, b1) ^ + base_gf->multiply.w32(base_gf, a1b1, h->prim_poly)) << 4)); + s8++; + d8++; + } + } else { + while (d8 < (uint8_t *) rd.d_top) { + a0 = *s8 & 0x0f; + a1 = (*s8 & 0xf0) >> 4; + a1b1 = base_gf->multiply.w32(base_gf, a1, b1); + + *d8 = ((base_gf->multiply.w32(base_gf, a0, b0) ^ a1b1) | + ((base_gf->multiply.w32(base_gf, a1, b0) ^ + base_gf->multiply.w32(base_gf, a0, b1) ^ + base_gf->multiply.w32(base_gf, a1b1, h->prim_poly)) << 4)); + s8++; + d8++; + } + } + } else { + if (xor) { + while (d8 < (uint8_t *) rd.d_top) { + a0 = *s8 & 0x0f; + a1 = (*s8 & 0xf0) >> 4; + a1b1 = GF_W4_INLINE_MULTDIV(mt, a1, b1); + + *d8 ^= ((GF_W4_INLINE_MULTDIV(mt, a0, b0) ^ a1b1) | + ((GF_W4_INLINE_MULTDIV(mt, a1, b0) ^ + GF_W4_INLINE_MULTDIV(mt, a0, b1) ^ + GF_W4_INLINE_MULTDIV(mt, a1b1, h->prim_poly)) << 4)); + s8++; + d8++; + } + } else { + while (d8 < (uint8_t *) rd.d_top) { + a0 = *s8 & 0x0f; + a1 = (*s8 & 0xf0) >> 4; + a1b1 = GF_W4_INLINE_MULTDIV(mt, a1, b1); + + *d8 = ((GF_W4_INLINE_MULTDIV(mt, a0, b0) ^ a1b1) | + ((GF_W4_INLINE_MULTDIV(mt, a1, b0) ^ + GF_W4_INLINE_MULTDIV(mt, a0, b1) ^ + GF_W4_INLINE_MULTDIV(mt, a1b1, h->prim_poly)) << 4)); + s8++; + d8++; + } + } + } + gf_do_final_region_alignment(&rd); + return; +} + +static +int gf_w8_composite_init(gf_t *gf) +{ + gf_internal_t *h = (gf_internal_t *) gf->scratch; + struct gf_w8_composite_data *cd; + + if (h->base_gf == NULL) return 0; + + cd = (struct gf_w8_composite_data *) h->private; + cd->mult_table = gf_w4_get_mult_table(h->base_gf); + + if (h->region_type & GF_REGION_ALTMAP) { + SET_FUNCTION(gf,multiply_region,w32,gf_w8_composite_multiply_region_alt) + } else { + SET_FUNCTION(gf,multiply_region,w32,gf_w8_composite_multiply_region) + } + + if (cd->mult_table == NULL) { + SET_FUNCTION(gf,multiply,w32,gf_w8_composite_multiply_recursive) + } else { + SET_FUNCTION(gf,multiply,w32,gf_w8_composite_multiply_inline) + } + SET_FUNCTION(gf,divide,w32,NULL) + SET_FUNCTION(gf,inverse,w32,gf_w8_composite_inverse) + + return 1; +} + +static +inline + gf_val_32_t +gf_w8_bytwo_p_multiply (gf_t *gf, gf_val_32_t a, gf_val_32_t b) +{ + uint32_t prod, pp, pmask, amask; + gf_internal_t *h; + + h = (gf_internal_t *) gf->scratch; + pp = h->prim_poly; + + + prod = 0; + pmask = 0x80; + amask = 0x80; + + while (amask != 0) { + if (prod & pmask) { + prod = ((prod << 1) ^ pp); + } else { + prod <<= 1; + } + if (a & amask) prod ^= b; + amask >>= 1; + } + return prod; +} + +static +inline + gf_val_32_t +gf_w8_bytwo_b_multiply (gf_t *gf, gf_val_32_t a, gf_val_32_t b) +{ + uint32_t prod, pp, bmask; + gf_internal_t *h; + + h = (gf_internal_t *) gf->scratch; + pp = h->prim_poly; + + prod = 0; + bmask = 0x80; + + while (1) { + if (a & 1) prod ^= b; + a >>= 1; + if (a == 0) return prod; + if (b & bmask) { + b = ((b << 1) ^ pp); + } else { + b <<= 1; + } + } +} + +static + void +gf_w8_bytwo_p_nosse_multiply_region(gf_t *gf, void *src, void *dest, gf_val_32_t val, int bytes, int xor) +{ + uint64_t *s64, *d64, t1, t2, ta, prod, amask; + gf_region_data rd; + struct gf_w8_bytwo_data *btd; + + if (val == 0) { gf_multby_zero(dest, bytes, xor); return; } + if (val == 1) { gf_multby_one(src, dest, bytes, xor); return; } + + btd = (struct gf_w8_bytwo_data *) ((gf_internal_t *) (gf->scratch))->private; + + gf_set_region_data(&rd, gf, src, dest, bytes, val, xor, 8); + gf_do_initial_region_alignment(&rd); + + s64 = (uint64_t *) rd.s_start; + d64 = (uint64_t *) rd.d_start; + + if (xor) { + while (s64 < (uint64_t *) rd.s_top) { + prod = 0; + amask = 0x80; + ta = *s64; + while (amask != 0) { + AB2(btd->prim_poly, btd->mask1, btd->mask2, prod, t1, t2); + if (val & amask) prod ^= ta; + amask >>= 1; + } + *d64 ^= prod; + d64++; + s64++; + } + } else { + while (s64 < (uint64_t *) rd.s_top) { + prod = 0; + amask = 0x80; + ta = *s64; + while (amask != 0) { + AB2(btd->prim_poly, btd->mask1, btd->mask2, prod, t1, t2); + if (val & amask) prod ^= ta; + amask >>= 1; + } + *d64 = prod; + d64++; + s64++; + } + } + gf_do_final_region_alignment(&rd); +} + +#define BYTWO_P_ONESTEP {\ + SSE_AB2(pp, m1 ,m2, prod, t1, t2); \ + t1 = _mm_and_si128(v, one); \ + t1 = _mm_sub_epi8(t1, one); \ + t1 = _mm_and_si128(t1, ta); \ + prod = _mm_xor_si128(prod, t1); \ + v = _mm_srli_epi64(v, 1); } + +#ifdef INTEL_SSE2 +static + void +gf_w8_bytwo_p_sse_multiply_region(gf_t *gf, void *src, void *dest, gf_val_32_t val, int bytes, int xor) +{ + int i; + uint8_t *s8, *d8; + uint8_t vrev; + __m128i pp, m1, m2, ta, prod, t1, t2, tp, one, v; + struct gf_w8_bytwo_data *btd; + gf_region_data rd; + + if (val == 0) { gf_multby_zero(dest, bytes, xor); return; } + if (val == 1) { gf_multby_one(src, dest, bytes, xor); return; } + + btd = (struct gf_w8_bytwo_data *) ((gf_internal_t *) (gf->scratch))->private; + + gf_set_region_data(&rd, gf, src, dest, bytes, val, xor, 16); + gf_do_initial_region_alignment(&rd); + + vrev = 0; + for (i = 0; i < 8; i++) { + vrev <<= 1; + if (!(val & (1 << i))) vrev |= 1; + } + + s8 = (uint8_t *) rd.s_start; + d8 = (uint8_t *) rd.d_start; + + pp = _mm_set1_epi8(btd->prim_poly&0xff); + m1 = _mm_set1_epi8((btd->mask1)&0xff); + m2 = _mm_set1_epi8((btd->mask2)&0xff); + one = _mm_set1_epi8(1); + + while (d8 < (uint8_t *) rd.d_top) { + prod = _mm_setzero_si128(); + v = _mm_set1_epi8(vrev); + ta = _mm_load_si128((__m128i *) s8); + tp = (!xor) ? _mm_setzero_si128() : _mm_load_si128((__m128i *) d8); + BYTWO_P_ONESTEP; + BYTWO_P_ONESTEP; + BYTWO_P_ONESTEP; + BYTWO_P_ONESTEP; + BYTWO_P_ONESTEP; + BYTWO_P_ONESTEP; + BYTWO_P_ONESTEP; + BYTWO_P_ONESTEP; + _mm_store_si128((__m128i *) d8, _mm_xor_si128(prod, tp)); + d8 += 16; + s8 += 16; + } + gf_do_final_region_alignment(&rd); +} +#endif + +#ifdef INTEL_SSE2 +static + void +gf_w8_bytwo_b_sse_region_2_noxor(gf_region_data *rd, struct gf_w8_bytwo_data *btd) +{ + uint8_t *d8, *s8; + __m128i pp, m1, m2, t1, t2, va; + + s8 = (uint8_t *) rd->s_start; + d8 = (uint8_t *) rd->d_start; + + pp = _mm_set1_epi8(btd->prim_poly&0xff); + m1 = _mm_set1_epi8((btd->mask1)&0xff); + m2 = _mm_set1_epi8((btd->mask2)&0xff); + + while (d8 < (uint8_t *) rd->d_top) { + va = _mm_load_si128 ((__m128i *)(s8)); + SSE_AB2(pp, m1, m2, va, t1, t2); + _mm_store_si128((__m128i *)d8, va); + d8 += 16; + s8 += 16; + } +} +#endif + +#ifdef INTEL_SSE2 +static + void +gf_w8_bytwo_b_sse_region_2_xor(gf_region_data *rd, struct gf_w8_bytwo_data *btd) +{ + uint8_t *d8, *s8; + __m128i pp, m1, m2, t1, t2, va, vb; + + s8 = (uint8_t *) rd->s_start; + d8 = (uint8_t *) rd->d_start; + + pp = _mm_set1_epi8(btd->prim_poly&0xff); + m1 = _mm_set1_epi8((btd->mask1)&0xff); + m2 = _mm_set1_epi8((btd->mask2)&0xff); + + while (d8 < (uint8_t *) rd->d_top) { + va = _mm_load_si128 ((__m128i *)(s8)); + SSE_AB2(pp, m1, m2, va, t1, t2); + vb = _mm_load_si128 ((__m128i *)(d8)); + vb = _mm_xor_si128(vb, va); + _mm_store_si128((__m128i *)d8, vb); + d8 += 16; + s8 += 16; + } +} +#endif + + +#ifdef INTEL_SSE2 +static + void +gf_w8_bytwo_b_sse_multiply_region(gf_t *gf, void *src, void *dest, gf_val_32_t val, int bytes, int xor) +{ + int itb; + uint8_t *d8, *s8; + __m128i pp, m1, m2, t1, t2, va, vb; + struct gf_w8_bytwo_data *btd; + gf_region_data rd; + + if (val == 0) { gf_multby_zero(dest, bytes, xor); return; } + if (val == 1) { gf_multby_one(src, dest, bytes, xor); return; } + + gf_set_region_data(&rd, gf, src, dest, bytes, val, xor, 16); + gf_do_initial_region_alignment(&rd); + + btd = (struct gf_w8_bytwo_data *) ((gf_internal_t *) (gf->scratch))->private; + + if (val == 2) { + if (xor) { + gf_w8_bytwo_b_sse_region_2_xor(&rd, btd); + } else { + gf_w8_bytwo_b_sse_region_2_noxor(&rd, btd); + } + gf_do_final_region_alignment(&rd); + return; + } + + s8 = (uint8_t *) rd.s_start; + d8 = (uint8_t *) rd.d_start; + + pp = _mm_set1_epi8(btd->prim_poly&0xff); + m1 = _mm_set1_epi8((btd->mask1)&0xff); + m2 = _mm_set1_epi8((btd->mask2)&0xff); + + while (d8 < (uint8_t *) rd.d_top) { + va = _mm_load_si128 ((__m128i *)(s8)); + vb = (!xor) ? _mm_setzero_si128() : _mm_load_si128 ((__m128i *)(d8)); + itb = val; + while (1) { + if (itb & 1) vb = _mm_xor_si128(vb, va); + itb >>= 1; + if (itb == 0) break; + SSE_AB2(pp, m1, m2, va, t1, t2); + } + _mm_store_si128((__m128i *)d8, vb); + d8 += 16; + s8 += 16; + } + + gf_do_final_region_alignment(&rd); +} +#endif + +static + void +gf_w8_bytwo_b_nosse_multiply_region(gf_t *gf, void *src, void *dest, gf_val_32_t val, int bytes, int xor) +{ + uint64_t *s64, *d64, t1, t2, ta, tb, prod; + struct gf_w8_bytwo_data *btd; + gf_region_data rd; + + if (val == 0) { gf_multby_zero(dest, bytes, xor); return; } + if (val == 1) { gf_multby_one(src, dest, bytes, xor); return; } + + gf_set_region_data(&rd, gf, src, dest, bytes, val, xor, 16); + gf_do_initial_region_alignment(&rd); + + btd = (struct gf_w8_bytwo_data *) ((gf_internal_t *) (gf->scratch))->private; + s64 = (uint64_t *) rd.s_start; + d64 = (uint64_t *) rd.d_start; + + switch (val) { + case 2: + if (xor) { + while (d64 < (uint64_t *) rd.d_top) { + ta = *s64; + AB2(btd->prim_poly, btd->mask1, btd->mask2, ta, t1, t2); + *d64 ^= ta; + d64++; + s64++; + } + } else { + while (d64 < (uint64_t *) rd.d_top) { + ta = *s64; + AB2(btd->prim_poly, btd->mask1, btd->mask2, ta, t1, t2); + *d64 = ta; + d64++; + s64++; + } + } + break; + case 3: + if (xor) { + while (d64 < (uint64_t *) rd.d_top) { + ta = *s64; + prod = ta; + AB2(btd->prim_poly, btd->mask1, btd->mask2, ta, t1, t2); + *d64 ^= (ta ^ prod); + d64++; + s64++; + } + } else { + while (d64 < (uint64_t *) rd.d_top) { + ta = *s64; + prod = ta; + AB2(btd->prim_poly, btd->mask1, btd->mask2, ta, t1, t2); + *d64 = (ta ^ prod); + d64++; + s64++; + } + } + break; + case 4: + if (xor) { + while (d64 < (uint64_t *) rd.d_top) { + ta = *s64; + AB2(btd->prim_poly, btd->mask1, btd->mask2, ta, t1, t2); + AB2(btd->prim_poly, btd->mask1, btd->mask2, ta, t1, t2); + *d64 ^= ta; + d64++; + s64++; + } + } else { + while (d64 < (uint64_t *) rd.d_top) { + ta = *s64; + AB2(btd->prim_poly, btd->mask1, btd->mask2, ta, t1, t2); + AB2(btd->prim_poly, btd->mask1, btd->mask2, ta, t1, t2); + *d64 = ta; + d64++; + s64++; + } + } + break; + case 5: + if (xor) { + while (d64 < (uint64_t *) rd.d_top) { + ta = *s64; + prod = ta; + AB2(btd->prim_poly, btd->mask1, btd->mask2, ta, t1, t2); + AB2(btd->prim_poly, btd->mask1, btd->mask2, ta, t1, t2); + *d64 ^= (ta ^ prod); + d64++; + s64++; + } + } else { + while (d64 < (uint64_t *) rd.d_top) { + ta = *s64; + prod = ta; + AB2(btd->prim_poly, btd->mask1, btd->mask2, ta, t1, t2); + AB2(btd->prim_poly, btd->mask1, btd->mask2, ta, t1, t2); + *d64 = ta ^ prod; + d64++; + s64++; + } + } + break; + case 6: + if (xor) { + while (d64 < (uint64_t *) rd.d_top) { + ta = *s64; + AB2(btd->prim_poly, btd->mask1, btd->mask2, ta, t1, t2); + prod = ta; + AB2(btd->prim_poly, btd->mask1, btd->mask2, ta, t1, t2); + *d64 ^= (ta ^ prod); + d64++; + s64++; + } + } else { + while (d64 < (uint64_t *) rd.d_top) { + ta = *s64; + AB2(btd->prim_poly, btd->mask1, btd->mask2, ta, t1, t2); + prod = ta; + AB2(btd->prim_poly, btd->mask1, btd->mask2, ta, t1, t2); + *d64 = ta ^ prod; + d64++; + s64++; + } + } + break; + /* + case 7: + if (xor) { + while (d64 < (uint64_t *) rd.d_top) { + ta = *s64; + prod = ta; + AB2(btd->prim_poly, btd->mask1, btd->mask2, ta, t1, t2); + prod ^= ta; + AB2(btd->prim_poly, btd->mask1, btd->mask2, ta, t1, t2); + *d64 ^= (ta ^ prod); + d64++; + s64++; + } + } else { + while (d64 < (uint64_t *) rd.d_top) { + ta = *s64; + prod = ta; + AB2(btd->prim_poly, btd->mask1, btd->mask2, ta, t1, t2); + prod ^= ta; + AB2(btd->prim_poly, btd->mask1, btd->mask2, ta, t1, t2); + *d64 = ta ^ prod; + d64++; + s64++; + } + } + break; + */ + case 8: + if (xor) { + while (d64 < (uint64_t *) rd.d_top) { + ta = *s64; + AB2(btd->prim_poly, btd->mask1, btd->mask2, ta, t1, t2); + AB2(btd->prim_poly, btd->mask1, btd->mask2, ta, t1, t2); + AB2(btd->prim_poly, btd->mask1, btd->mask2, ta, t1, t2); + *d64 ^= ta; + d64++; + s64++; + } + } else { + while (d64 < (uint64_t *) rd.d_top) { + ta = *s64; + AB2(btd->prim_poly, btd->mask1, btd->mask2, ta, t1, t2); + AB2(btd->prim_poly, btd->mask1, btd->mask2, ta, t1, t2); + AB2(btd->prim_poly, btd->mask1, btd->mask2, ta, t1, t2); + *d64 = ta; + d64++; + s64++; + } + } + break; + /* + case 9: + if (xor) { + while (d64 < (uint64_t *) rd.d_top) { + ta = *s64; + prod = ta; + AB2(btd->prim_poly, btd->mask1, btd->mask2, ta, t1, t2); + AB2(btd->prim_poly, btd->mask1, btd->mask2, ta, t1, t2); + AB2(btd->prim_poly, btd->mask1, btd->mask2, ta, t1, t2); + *d64 ^= (ta ^ prod); + d64++; + s64++; + } + } else { + while (d64 < (uint64_t *) rd.d_top) { + ta = *s64; + prod = ta; + AB2(btd->prim_poly, btd->mask1, btd->mask2, ta, t1, t2); + AB2(btd->prim_poly, btd->mask1, btd->mask2, ta, t1, t2); + AB2(btd->prim_poly, btd->mask1, btd->mask2, ta, t1, t2); + *d64 = (ta ^ prod); + d64++; + s64++; + } + } + break; + case 10: + if (xor) { + while (d64 < (uint64_t *) rd.d_top) { + ta = *s64; + AB2(btd->prim_poly, btd->mask1, btd->mask2, ta, t1, t2); + prod = ta; + AB2(btd->prim_poly, btd->mask1, btd->mask2, ta, t1, t2); + AB2(btd->prim_poly, btd->mask1, btd->mask2, ta, t1, t2); + *d64 ^= (ta ^ prod); + d64++; + s64++; + } + } else { + while (d64 < (uint64_t *) rd.d_top) { + ta = *s64; + AB2(btd->prim_poly, btd->mask1, btd->mask2, ta, t1, t2); + prod = ta; + AB2(btd->prim_poly, btd->mask1, btd->mask2, ta, t1, t2); + AB2(btd->prim_poly, btd->mask1, btd->mask2, ta, t1, t2); + *d64 = (ta ^ prod); + d64++; + s64++; + } + } + break; + case 11: + if (xor) { + while (d64 < (uint64_t *) rd.d_top) { + ta = *s64; + prod = ta; + AB2(btd->prim_poly, btd->mask1, btd->mask2, ta, t1, t2); + prod ^= ta; + AB2(btd->prim_poly, btd->mask1, btd->mask2, ta, t1, t2); + AB2(btd->prim_poly, btd->mask1, btd->mask2, ta, t1, t2); + *d64 ^= (ta ^ prod); + d64++; + s64++; + } + } else { + while (d64 < (uint64_t *) rd.d_top) { + ta = *s64; + prod = ta; + AB2(btd->prim_poly, btd->mask1, btd->mask2, ta, t1, t2); + prod ^= ta; + AB2(btd->prim_poly, btd->mask1, btd->mask2, ta, t1, t2); + AB2(btd->prim_poly, btd->mask1, btd->mask2, ta, t1, t2); + *d64 = (ta ^ prod); + d64++; + s64++; + } + } + break; + case 12: + if (xor) { + while (d64 < (uint64_t *) rd.d_top) { + ta = *s64; + AB2(btd->prim_poly, btd->mask1, btd->mask2, ta, t1, t2); + AB2(btd->prim_poly, btd->mask1, btd->mask2, ta, t1, t2); + prod = ta; + AB2(btd->prim_poly, btd->mask1, btd->mask2, ta, t1, t2); + *d64 ^= (ta ^ prod); + d64++; + s64++; + } + } else { + while (d64 < (uint64_t *) rd.d_top) { + ta = *s64; + AB2(btd->prim_poly, btd->mask1, btd->mask2, ta, t1, t2); + AB2(btd->prim_poly, btd->mask1, btd->mask2, ta, t1, t2); + prod = ta; + AB2(btd->prim_poly, btd->mask1, btd->mask2, ta, t1, t2); + *d64 = (ta ^ prod); + d64++; + s64++; + } + } + break; + case 13: + if (xor) { + while (d64 < (uint64_t *) rd.d_top) { + ta = *s64; + prod = ta; + AB2(btd->prim_poly, btd->mask1, btd->mask2, ta, t1, t2); + AB2(btd->prim_poly, btd->mask1, btd->mask2, ta, t1, t2); + prod ^= ta; + AB2(btd->prim_poly, btd->mask1, btd->mask2, ta, t1, t2); + *d64 ^= (ta ^ prod); + d64++; + s64++; + } + } else { + while (d64 < (uint64_t *) rd.d_top) { + ta = *s64; + prod = ta; + AB2(btd->prim_poly, btd->mask1, btd->mask2, ta, t1, t2); + AB2(btd->prim_poly, btd->mask1, btd->mask2, ta, t1, t2); + prod ^= ta; + AB2(btd->prim_poly, btd->mask1, btd->mask2, ta, t1, t2); + *d64 = (ta ^ prod); + d64++; + s64++; + } + } + break; + case 14: + if (xor) { + while (d64 < (uint64_t *) rd.d_top) { + ta = *s64; + AB2(btd->prim_poly, btd->mask1, btd->mask2, ta, t1, t2); + prod = ta; + AB2(btd->prim_poly, btd->mask1, btd->mask2, ta, t1, t2); + prod ^= ta; + AB2(btd->prim_poly, btd->mask1, btd->mask2, ta, t1, t2); + *d64 ^= (ta ^ prod); + d64++; + s64++; + } + } else { + while (d64 < (uint64_t *) rd.d_top) { + ta = *s64; + AB2(btd->prim_poly, btd->mask1, btd->mask2, ta, t1, t2); + prod = ta; + AB2(btd->prim_poly, btd->mask1, btd->mask2, ta, t1, t2); + prod ^= ta; + AB2(btd->prim_poly, btd->mask1, btd->mask2, ta, t1, t2); + *d64 = (ta ^ prod); + d64++; + s64++; + } + } + break; + case 15: + if (xor) { + while (d64 < (uint64_t *) rd.d_top) { + ta = *s64; + prod = ta; + AB2(btd->prim_poly, btd->mask1, btd->mask2, ta, t1, t2); + prod ^= ta; + AB2(btd->prim_poly, btd->mask1, btd->mask2, ta, t1, t2); + prod ^= ta; + AB2(btd->prim_poly, btd->mask1, btd->mask2, ta, t1, t2); + *d64 ^= (ta ^ prod); + d64++; + s64++; + } + } else { + while (d64 < (uint64_t *) rd.d_top) { + ta = *s64; + prod = ta; + AB2(btd->prim_poly, btd->mask1, btd->mask2, ta, t1, t2); + prod ^= ta; + AB2(btd->prim_poly, btd->mask1, btd->mask2, ta, t1, t2); + prod ^= ta; + AB2(btd->prim_poly, btd->mask1, btd->mask2, ta, t1, t2); + *d64 = (ta ^ prod); + d64++; + s64++; + } + } + break; + */ + default: + if (xor) { + while (d64 < (uint64_t *) rd.d_top) { + prod = *d64 ; + ta = *s64; + tb = val; + while (1) { + if (tb & 1) prod ^= ta; + tb >>= 1; + if (tb == 0) break; + AB2(btd->prim_poly, btd->mask1, btd->mask2, ta, t1, t2); + } + *d64 = prod; + d64++; + s64++; + } + } else { + while (d64 < (uint64_t *) rd.d_top) { + prod = 0 ; + ta = *s64; + tb = val; + while (1) { + if (tb & 1) prod ^= ta; + tb >>= 1; + if (tb == 0) break; + AB2(btd->prim_poly, btd->mask1, btd->mask2, ta, t1, t2); + } + *d64 = prod; + d64++; + s64++; + } + } + break; + } + gf_do_final_region_alignment(&rd); +} + + static +int gf_w8_bytwo_init(gf_t *gf) +{ + gf_internal_t *h; + uint64_t ip, m1, m2; + struct gf_w8_bytwo_data *btd; + + h = (gf_internal_t *) gf->scratch; + btd = (struct gf_w8_bytwo_data *) (h->private); + ip = h->prim_poly & 0xff; + m1 = 0xfe; + m2 = 0x80; + btd->prim_poly = 0; + btd->mask1 = 0; + btd->mask2 = 0; + + while (ip != 0) { + btd->prim_poly |= ip; + btd->mask1 |= m1; + btd->mask2 |= m2; + ip <<= GF_FIELD_WIDTH; + m1 <<= GF_FIELD_WIDTH; + m2 <<= GF_FIELD_WIDTH; + } + + if (h->mult_type == GF_MULT_BYTWO_p) { + SET_FUNCTION(gf,multiply,w32,gf_w8_bytwo_p_multiply) +#ifdef INTEL_SSE2 + if (gf_cpu_supports_intel_sse2 && !(h->region_type & GF_REGION_NOSIMD)) { + SET_FUNCTION(gf,multiply_region,w32,gf_w8_bytwo_p_sse_multiply_region) + } else { +#endif + SET_FUNCTION(gf,multiply_region,w32,gf_w8_bytwo_p_nosse_multiply_region) + if(h->region_type & GF_REGION_SIMD) + return 0; +#ifdef INTEL_SSE2 + } +#endif + } else { + SET_FUNCTION(gf,multiply,w32,gf_w8_bytwo_b_multiply) +#ifdef INTEL_SSE2 + if (gf_cpu_supports_intel_sse2 && !(h->region_type & GF_REGION_NOSIMD)) { + SET_FUNCTION(gf,multiply_region,w32,gf_w8_bytwo_b_sse_multiply_region) + } else { +#endif + SET_FUNCTION(gf,multiply_region,w32,gf_w8_bytwo_b_nosse_multiply_region) + if(h->region_type & GF_REGION_SIMD) + return 0; +#ifdef INTEL_SSE2 + } +#endif + } + return 1; +} + + +/* ------------------------------------------------------------ + General procedures. + You don't need to error check here on in init, because it's done + for you in gf_error_check(). + */ + +int gf_w8_scratch_size(int mult_type, int region_type, int divide_type, int arg1, int arg2) +{ + switch(mult_type) + { + case GF_MULT_DEFAULT: + if (gf_cpu_supports_intel_ssse3 || gf_cpu_supports_arm_neon) { + return sizeof(gf_internal_t) + sizeof(struct gf_w8_default_data) + 64; + } + return sizeof(gf_internal_t) + sizeof(struct gf_w8_single_table_data) + 64; + case GF_MULT_TABLE: + if (region_type == GF_REGION_CAUCHY) { + return sizeof(gf_internal_t) + sizeof(struct gf_w8_single_table_data) + 64; + } + + if (region_type == GF_REGION_DEFAULT) { + return sizeof(gf_internal_t) + sizeof(struct gf_w8_single_table_data) + 64; + } + if (region_type & GF_REGION_DOUBLE_TABLE) { + if (region_type == GF_REGION_DOUBLE_TABLE) { + return sizeof(gf_internal_t) + sizeof(struct gf_w8_double_table_data) + 64; + } else if (region_type == (GF_REGION_DOUBLE_TABLE | GF_REGION_LAZY)) { + return sizeof(gf_internal_t) + sizeof(struct gf_w8_double_table_lazy_data) + 64; + } else { + return 0; + } + } + return 0; + break; + case GF_MULT_BYTWO_p: + case GF_MULT_BYTWO_b: + return sizeof(gf_internal_t) + sizeof(struct gf_w8_bytwo_data); + break; + case GF_MULT_SPLIT_TABLE: + if ((arg1 == 4 && arg2 == 8) || (arg1 == 8 && arg2 == 4)) { + return sizeof(gf_internal_t) + sizeof(struct gf_w8_half_table_data) + 64; + } + break; + case GF_MULT_LOG_TABLE: + return sizeof(gf_internal_t) + sizeof(struct gf_w8_logtable_data) + 64; + break; + case GF_MULT_LOG_ZERO: + return sizeof(gf_internal_t) + sizeof(struct gf_w8_logzero_small_table_data) + 64; + break; + case GF_MULT_LOG_ZERO_EXT: + return sizeof(gf_internal_t) + sizeof(struct gf_w8_logzero_table_data) + 64; + break; + case GF_MULT_CARRY_FREE: + return sizeof(gf_internal_t); + break; + case GF_MULT_SHIFT: + return sizeof(gf_internal_t); + break; + case GF_MULT_COMPOSITE: + return sizeof(gf_internal_t) + sizeof(struct gf_w8_composite_data) + 64; + default: + return 0; + } + return 0; +} + +int gf_w8_init(gf_t *gf) +{ + gf_internal_t *h; + + h = (gf_internal_t *) gf->scratch; + + /* Allen: set default primitive polynomial / irreducible polynomial if needed */ + + if (h->prim_poly == 0) { + if (h->mult_type == GF_MULT_COMPOSITE) { + h->prim_poly = gf_composite_get_default_poly(h->base_gf); + if (h->prim_poly == 0) return 0; /* JSP: This shouldn't happen, but just in case. */ + } else { + h->prim_poly = 0x11d; + } + } + if (h->mult_type != GF_MULT_COMPOSITE) { + h->prim_poly |= 0x100; + } + + SET_FUNCTION(gf,multiply,w32,NULL) + SET_FUNCTION(gf,divide,w32,NULL) + SET_FUNCTION(gf,inverse,w32,NULL) + SET_FUNCTION(gf,multiply_region,w32,NULL) + SET_FUNCTION(gf,extract_word,w32,gf_w8_extract_word) + + switch(h->mult_type) { + case GF_MULT_DEFAULT: + case GF_MULT_TABLE: if (gf_w8_table_init(gf) == 0) return 0; break; + case GF_MULT_BYTWO_p: + case GF_MULT_BYTWO_b: if (gf_w8_bytwo_init(gf) == 0) return 0; break; + case GF_MULT_LOG_ZERO: + case GF_MULT_LOG_ZERO_EXT: + case GF_MULT_LOG_TABLE: if (gf_w8_log_init(gf) == 0) return 0; break; + case GF_MULT_CARRY_FREE: if (gf_w8_cfm_init(gf) == 0) return 0; break; + case GF_MULT_SHIFT: if (gf_w8_shift_init(gf) == 0) return 0; break; + case GF_MULT_SPLIT_TABLE: if (gf_w8_split_init(gf) == 0) return 0; break; + case GF_MULT_COMPOSITE: if (gf_w8_composite_init(gf) == 0) return 0; break; + default: return 0; + } + + if (h->divide_type == GF_DIVIDE_EUCLID) { + SET_FUNCTION(gf,divide,w32,gf_w8_divide_from_inverse) + SET_FUNCTION(gf,inverse,w32,gf_w8_euclid) + } else if (h->divide_type == GF_DIVIDE_MATRIX) { + SET_FUNCTION(gf,divide,w32,gf_w8_divide_from_inverse) + SET_FUNCTION(gf,inverse,w32,gf_w8_matrix) + } + + if (gf->divide.w32 == NULL) { + SET_FUNCTION(gf,divide,w32,gf_w8_divide_from_inverse) + if (gf->inverse.w32 == NULL) SET_FUNCTION(gf,inverse,w32,gf_w8_euclid) + } + + if (gf->inverse.w32 == NULL) SET_FUNCTION(gf,inverse,w32,gf_w8_inverse_from_divide) + + if (h->mult_type == GF_MULT_COMPOSITE && (h->region_type & GF_REGION_ALTMAP)) { + SET_FUNCTION(gf,extract_word,w32,gf_w8_composite_extract_word) + } + + if (h->region_type == GF_REGION_CAUCHY) { + SET_FUNCTION(gf,multiply_region,w32,gf_wgen_cauchy_region) + SET_FUNCTION(gf,extract_word,w32,gf_wgen_extract_word) + } + + if (gf->multiply_region.w32 == NULL) { + SET_FUNCTION(gf,multiply_region,w32,gf_w8_multiply_region_from_single) + } + + return 1; +} + + +/* Inline setup functions */ + +uint8_t *gf_w8_get_mult_table(gf_t *gf) +{ + gf_internal_t *h; + struct gf_w8_default_data *ftd; + struct gf_w8_single_table_data *std; + + h = (gf_internal_t *) gf->scratch; + if (gf->multiply.w32 == gf_w8_default_multiply) { + ftd = (struct gf_w8_default_data *) h->private; + return (uint8_t *) ftd->multtable; + } else if (gf->multiply.w32 == gf_w8_table_multiply) { + std = (struct gf_w8_single_table_data *) h->private; + return (uint8_t *) std->multtable; + } + return NULL; +} + +uint8_t *gf_w8_get_div_table(gf_t *gf) +{ + struct gf_w8_default_data *ftd; + struct gf_w8_single_table_data *std; + + if (gf->multiply.w32 == gf_w8_default_multiply) { + ftd = (struct gf_w8_default_data *) ((gf_internal_t *) gf->scratch)->private; + return (uint8_t *) ftd->divtable; + } else if (gf->multiply.w32 == gf_w8_table_multiply) { + std = (struct gf_w8_single_table_data *) ((gf_internal_t *) gf->scratch)->private; + return (uint8_t *) std->divtable; + } + return NULL; +} diff --git a/IDA_new/gf-complete/src/gf_wgen.c b/IDA_new/gf-complete/src/gf_wgen.c new file mode 100644 index 0000000..1e3d2e0 --- /dev/null +++ b/IDA_new/gf-complete/src/gf_wgen.c @@ -0,0 +1,1019 @@ +/* + * GF-Complete: A Comprehensive Open Source Library for Galois Field Arithmetic + * James S. Plank, Ethan L. Miller, Kevin M. Greenan, + * Benjamin A. Arnold, John A. Burnum, Adam W. Disney, Allen C. McBride. + * + * gf_wgen.c + * + * Routines for Galois fields for general w < 32. For specific w, + like 4, 8, 16, 32, 64 and 128, see the other files. + */ + +#include "gf_int.h" +#include +#include + +struct gf_wgen_table_w8_data { + uint8_t *mult; + uint8_t *div; + uint8_t base; +}; + +struct gf_wgen_table_w16_data { + uint16_t *mult; + uint16_t *div; + uint16_t base; +}; + +struct gf_wgen_log_w8_data { + uint8_t *log; + uint8_t *anti; + uint8_t *danti; + uint8_t base; +}; + +struct gf_wgen_log_w16_data { + uint16_t *log; + uint16_t *anti; + uint16_t *danti; + uint16_t base; +}; + +struct gf_wgen_log_w32_data { + uint32_t *log; + uint32_t *anti; + uint32_t *danti; + uint32_t base; +}; + +struct gf_wgen_group_data { + uint32_t *reduce; + uint32_t *shift; + uint32_t mask; + uint64_t rmask; + int tshift; + uint32_t memory; +}; + +static +inline +gf_val_32_t gf_wgen_inverse_from_divide (gf_t *gf, gf_val_32_t a) +{ + return gf->divide.w32(gf, 1, a); +} + +static +inline +gf_val_32_t gf_wgen_divide_from_inverse (gf_t *gf, gf_val_32_t a, gf_val_32_t b) +{ + b = gf->inverse.w32(gf, b); + return gf->multiply.w32(gf, a, b); +} + +static +inline +gf_val_32_t gf_wgen_euclid (gf_t *gf, gf_val_32_t b) +{ + + gf_val_32_t e_i, e_im1, e_ip1; + gf_val_32_t d_i, d_im1, d_ip1; + gf_val_32_t y_i, y_im1, y_ip1; + gf_val_32_t c_i; + + if (b == 0) return -1; + e_im1 = ((gf_internal_t *) (gf->scratch))->prim_poly; + e_i = b; + d_im1 = ((gf_internal_t *) (gf->scratch))->w; + for (d_i = d_im1; ((1 << d_i) & e_i) == 0; d_i--) ; + y_i = 1; + y_im1 = 0; + + while (e_i != 1) { + + e_ip1 = e_im1; + d_ip1 = d_im1; + c_i = 0; + + while (d_ip1 >= d_i) { + c_i ^= (1 << (d_ip1 - d_i)); + e_ip1 ^= (e_i << (d_ip1 - d_i)); + if (e_ip1 == 0) return 0; + while ((e_ip1 & (1 << d_ip1)) == 0) d_ip1--; + } + + y_ip1 = y_im1 ^ gf->multiply.w32(gf, c_i, y_i); + y_im1 = y_i; + y_i = y_ip1; + + e_im1 = e_i; + d_im1 = d_i; + e_i = e_ip1; + d_i = d_ip1; + } + + return y_i; +} + +gf_val_32_t gf_wgen_extract_word(gf_t *gf, void *start, int bytes, int index) +{ + uint8_t *ptr; + uint32_t rv; + int rs; + int byte, bit, i; + gf_internal_t *h; + + h = (gf_internal_t *) gf->scratch; + rs = bytes / h->w; + byte = index/8; + bit = index%8; + + ptr = (uint8_t *) start; + ptr += bytes; + ptr -= rs; + ptr += byte; + + rv = 0; + for (i = 0; i < h->w; i++) { + rv <<= 1; + if ((*ptr) & (1 << bit)) rv |= 1; + ptr -= rs; + } + + return rv; +} + +static +inline +gf_val_32_t gf_wgen_matrix (gf_t *gf, gf_val_32_t b) +{ + return gf_bitmatrix_inverse(b, ((gf_internal_t *) (gf->scratch))->w, + ((gf_internal_t *) (gf->scratch))->prim_poly); +} + +static +inline +uint32_t +gf_wgen_shift_multiply (gf_t *gf, uint32_t a32, uint32_t b32) +{ + uint64_t product, i, pp, a, b, one; + gf_internal_t *h; + + a = a32; + b = b32; + h = (gf_internal_t *) gf->scratch; + one = 1; + pp = h->prim_poly | (one << h->w); + + product = 0; + + for (i = 0; i < (uint64_t)h->w; i++) { + if (a & (one << i)) product ^= (b << i); + } + for (i = h->w*2-1; i >= (uint64_t)h->w; i--) { + if (product & (one << i)) product ^= (pp << (i-h->w)); + } + return product; +} + +static +int gf_wgen_shift_init(gf_t *gf) +{ + SET_FUNCTION(gf,multiply,w32,gf_wgen_shift_multiply) + SET_FUNCTION(gf,inverse,w32,gf_wgen_euclid) + return 1; +} + +static +gf_val_32_t +gf_wgen_bytwo_b_multiply (gf_t *gf, gf_val_32_t a, gf_val_32_t b) +{ + uint32_t prod, pp, bmask; + gf_internal_t *h; + + h = (gf_internal_t *) gf->scratch; + pp = h->prim_poly; + + prod = 0; + bmask = (1 << (h->w-1)); + + while (1) { + if (a & 1) prod ^= b; + a >>= 1; + if (a == 0) return prod; + if (b & bmask) { + b = ((b << 1) ^ pp); + } else { + b <<= 1; + } + } +} + +static +int gf_wgen_bytwo_b_init(gf_t *gf) +{ + SET_FUNCTION(gf,multiply,w32,gf_wgen_bytwo_b_multiply) + SET_FUNCTION(gf,inverse,w32,gf_wgen_euclid) + return 1; +} + +static +inline +gf_val_32_t +gf_wgen_bytwo_p_multiply (gf_t *gf, gf_val_32_t a, gf_val_32_t b) +{ + uint32_t prod, pp, pmask, amask; + gf_internal_t *h; + + h = (gf_internal_t *) gf->scratch; + pp = h->prim_poly; + + prod = 0; + pmask = (1 << ((h->w)-1)); /*Ben: Had an operator precedence warning here*/ + amask = pmask; + + while (amask != 0) { + if (prod & pmask) { + prod = ((prod << 1) ^ pp); + } else { + prod <<= 1; + } + if (a & amask) prod ^= b; + amask >>= 1; + } + return prod; +} + + +static +int gf_wgen_bytwo_p_init(gf_t *gf) +{ + SET_FUNCTION(gf,multiply,w32,gf_wgen_bytwo_p_multiply) + SET_FUNCTION(gf,inverse,w32,gf_wgen_euclid) + return 1; +} + +static +void +gf_wgen_group_set_shift_tables(uint32_t *shift, uint32_t val, gf_internal_t *h) +{ + uint32_t i; + uint32_t j; + int g_s; + + if (h->mult_type == GF_MULT_DEFAULT) { + g_s = 2; + } else { + g_s = h->arg1; + } + + shift[0] = 0; + + for (i = 1; i < ((uint32_t)1 << g_s); i <<= 1) { + for (j = 0; j < i; j++) shift[i|j] = shift[j]^val; + if (val & (1 << (h->w-1))) { + val <<= 1; + val ^= h->prim_poly; + } else { + val <<= 1; + } + } +} + +static +inline +gf_val_32_t +gf_wgen_group_s_equals_r_multiply(gf_t *gf, gf_val_32_t a, gf_val_32_t b) +{ + int leftover, rs; + uint32_t p, l, ind, a32; + int bits_left; + int g_s; + int w; + + struct gf_wgen_group_data *gd; + gf_internal_t *h = (gf_internal_t *) gf->scratch; + g_s = h->arg1; + w = h->w; + + gd = (struct gf_wgen_group_data *) h->private; + gf_wgen_group_set_shift_tables(gd->shift, b, h); + + leftover = w % g_s; + if (leftover == 0) leftover = g_s; + + rs = w - leftover; + a32 = a; + ind = a32 >> rs; + a32 <<= leftover; + a32 &= gd->mask; + p = gd->shift[ind]; + + bits_left = rs; + rs = w - g_s; + + while (bits_left > 0) { + bits_left -= g_s; + ind = a32 >> rs; + a32 <<= g_s; + a32 &= gd->mask; + l = p >> rs; + p = (gd->shift[ind] ^ gd->reduce[l] ^ (p << g_s)) & gd->mask; + } + return p; +} + +char *bits(uint32_t v) +{ + char *rv; + int i, j; + + rv = malloc(30); + j = 0; + for (i = 27; i >= 0; i--) { + rv[j] = '0' + ((v & (1 << i)) ? 1 : 0); + j++; + } + rv[j] = '\0'; + return rv; +} +char *bits_56(uint64_t v) +{ + char *rv; + int i, j; + uint64_t one; + + one = 1; + + rv = malloc(60); + j = 0; + for (i = 55; i >= 0; i--) { + rv[j] = '0' + ((v & (one << i)) ? 1 : 0); + j++; + } + rv[j] = '\0'; + return rv; +} + +static +inline +gf_val_32_t +gf_wgen_group_multiply(gf_t *gf, gf_val_32_t a, gf_val_32_t b) +{ + int i; + int leftover; + uint64_t p, l, r; + uint32_t a32, ind; + int g_s, g_r; + struct gf_wgen_group_data *gd; + int w; + + gf_internal_t *h = (gf_internal_t *) gf->scratch; + if (h->mult_type == GF_MULT_DEFAULT) { + g_s = 2; + g_r = 8; + } else { + g_s = h->arg1; + g_r = h->arg2; + } + w = h->w; + gd = (struct gf_wgen_group_data *) h->private; + gf_wgen_group_set_shift_tables(gd->shift, b, h); + + leftover = w % g_s; + if (leftover == 0) leftover = g_s; + + a32 = a; + ind = a32 >> (w - leftover); + p = gd->shift[ind]; + p <<= g_s; + a32 <<= leftover; + a32 &= gd->mask; + + i = (w - leftover); + while (i > g_s) { + ind = a32 >> (w-g_s); + p ^= gd->shift[ind]; + a32 <<= g_s; + a32 &= gd->mask; + p <<= g_s; + i -= g_s; + } + + ind = a32 >> (h->w-g_s); + p ^= gd->shift[ind]; + + for (i = gd->tshift ; i >= 0; i -= g_r) { + l = p & (gd->rmask << i); + r = gd->reduce[l >> (i+w)]; + r <<= (i); + p ^= r; + } + return p & gd->mask; +} + +static +int gf_wgen_group_init(gf_t *gf) +{ + uint32_t i, j, p, index; + struct gf_wgen_group_data *gd; + gf_internal_t *h = (gf_internal_t *) gf->scratch; + uint32_t g_s, g_r; + + if (h->mult_type == GF_MULT_DEFAULT) { + g_s = 2; + g_r = 8; + } else { + g_s = h->arg1; + g_r = h->arg2; + } + gd = (struct gf_wgen_group_data *) h->private; + gd->shift = &(gd->memory); + gd->reduce = gd->shift + (1 << g_s); + gd->mask = (h->w != 31) ? ((1 << h->w)-1) : 0x7fffffff; + + gd->rmask = (1 << g_r) - 1; + gd->rmask <<= h->w; + + gd->tshift = h->w % g_s; + if (gd->tshift == 0) gd->tshift = g_s; + gd->tshift = (h->w - gd->tshift); + gd->tshift = ((gd->tshift-1)/g_r) * g_r; + + gd->reduce[0] = 0; + for (i = 0; i < ((uint32_t)1 << g_r); i++) { + p = 0; + index = 0; + for (j = 0; j < g_r; j++) { + if (i & (1 << j)) { + p ^= (h->prim_poly << j); + index ^= (h->prim_poly >> (h->w-j)); + } + } + gd->reduce[index] = (p & gd->mask); + } + + if (g_s == g_r) { + SET_FUNCTION(gf,multiply,w32,gf_wgen_group_s_equals_r_multiply) + } else { + SET_FUNCTION(gf,multiply,w32,gf_wgen_group_multiply) + } + SET_FUNCTION(gf,divide,w32,NULL) + SET_FUNCTION(gf,divide,w32,NULL) + return 1; +} + + +static +gf_val_32_t +gf_wgen_table_8_multiply(gf_t *gf, gf_val_32_t a, gf_val_32_t b) +{ + gf_internal_t *h; + struct gf_wgen_table_w8_data *std; + + h = (gf_internal_t *) gf->scratch; + std = (struct gf_wgen_table_w8_data *) h->private; + + return (std->mult[(a<w)+b]); +} + +static +gf_val_32_t +gf_wgen_table_8_divide(gf_t *gf, gf_val_32_t a, gf_val_32_t b) +{ + gf_internal_t *h; + struct gf_wgen_table_w8_data *std; + + h = (gf_internal_t *) gf->scratch; + std = (struct gf_wgen_table_w8_data *) h->private; + + return (std->div[(a<w)+b]); +} + +static +int gf_wgen_table_8_init(gf_t *gf) +{ + gf_internal_t *h; + int w; + struct gf_wgen_table_w8_data *std; + uint32_t a, b, p; + + h = (gf_internal_t *) gf->scratch; + w = h->w; + std = (struct gf_wgen_table_w8_data *) h->private; + + std->mult = &(std->base); + std->div = std->mult + ((1<w)*(1<w)); + + for (a = 0; a < ((uint32_t)1 << w); a++) { + std->mult[a] = 0; + std->mult[a<div[a] = 0; + std->div[a<mult[(a<div[(p<scratch; + std = (struct gf_wgen_table_w16_data *) h->private; + + return (std->mult[(a<w)+b]); +} + +static +gf_val_32_t +gf_wgen_table_16_divide(gf_t *gf, gf_val_32_t a, gf_val_32_t b) +{ + gf_internal_t *h; + struct gf_wgen_table_w16_data *std; + + h = (gf_internal_t *) gf->scratch; + std = (struct gf_wgen_table_w16_data *) h->private; + + return (std->div[(a<w)+b]); +} + +static +int gf_wgen_table_16_init(gf_t *gf) +{ + gf_internal_t *h; + int w; + struct gf_wgen_table_w16_data *std; + uint32_t a, b, p; + + h = (gf_internal_t *) gf->scratch; + w = h->w; + std = (struct gf_wgen_table_w16_data *) h->private; + + std->mult = &(std->base); + std->div = std->mult + ((1<w)*(1<w)); + + for (a = 0; a < ((uint32_t)1 << w); a++) { + std->mult[a] = 0; + std->mult[a<div[a] = 0; + std->div[a<mult[(a<div[(p<scratch; + if (h->w <= 8) return gf_wgen_table_8_init(gf); + if (h->w <= 14) return gf_wgen_table_16_init(gf); + + /* Returning zero to make the compiler happy, but this won't get + executed, because it is tested in _scratch_space. */ + + return 0; +} + +static +gf_val_32_t +gf_wgen_log_8_multiply(gf_t *gf, gf_val_32_t a, gf_val_32_t b) +{ + gf_internal_t *h; + struct gf_wgen_log_w8_data *std; + + h = (gf_internal_t *) gf->scratch; + std = (struct gf_wgen_log_w8_data *) h->private; + + if (a == 0 || b == 0) return 0; + return (std->anti[std->log[a]+std->log[b]]); +} + +static +gf_val_32_t +gf_wgen_log_8_divide(gf_t *gf, gf_val_32_t a, gf_val_32_t b) +{ + gf_internal_t *h; + struct gf_wgen_log_w8_data *std; + int index; + + h = (gf_internal_t *) gf->scratch; + std = (struct gf_wgen_log_w8_data *) h->private; + + if (a == 0 || b == 0) return 0; + index = std->log[a]; + index -= std->log[b]; + + return (std->danti[index]); +} + +static +int gf_wgen_log_8_init(gf_t *gf) +{ + gf_internal_t *h; + struct gf_wgen_log_w8_data *std; + int w; + uint32_t a, i; + int check = 0; + + h = (gf_internal_t *) gf->scratch; + w = h->w; + std = (struct gf_wgen_log_w8_data *) h->private; + + std->log = &(std->base); + std->anti = std->log + (1<w); + std->danti = std->anti + (1<w)-1; + + for (i = 0; i < ((uint32_t)1 << w); i++) + std->log[i] = 0; + + a = 1; + for(i=0; i < ((uint32_t)1<log[a] != 0) check = 1; + std->log[a] = i; + std->anti[i] = a; + std->danti[i] = a; + a <<= 1; + if(a & (1<prim_poly; + //a &= ((1 << w)-1); + } + + if (check != 0) { + _gf_errno = GF_E_LOGPOLY; + return 0; + } + + SET_FUNCTION(gf,multiply,w32,gf_wgen_log_8_multiply) + SET_FUNCTION(gf,divide,w32,gf_wgen_log_8_divide) + return 1; +} + +static +gf_val_32_t +gf_wgen_log_16_multiply(gf_t *gf, gf_val_32_t a, gf_val_32_t b) +{ + gf_internal_t *h; + struct gf_wgen_log_w16_data *std; + + h = (gf_internal_t *) gf->scratch; + std = (struct gf_wgen_log_w16_data *) h->private; + + if (a == 0 || b == 0) return 0; + return (std->anti[std->log[a]+std->log[b]]); +} + +static +gf_val_32_t +gf_wgen_log_16_divide(gf_t *gf, gf_val_32_t a, gf_val_32_t b) +{ + gf_internal_t *h; + struct gf_wgen_log_w16_data *std; + int index; + + h = (gf_internal_t *) gf->scratch; + std = (struct gf_wgen_log_w16_data *) h->private; + + if (a == 0 || b == 0) return 0; + index = std->log[a]; + index -= std->log[b]; + + return (std->danti[index]); +} + +static +int gf_wgen_log_16_init(gf_t *gf) +{ + gf_internal_t *h; + struct gf_wgen_log_w16_data *std; + int w; + uint32_t a, i; + int check = 0; + + h = (gf_internal_t *) gf->scratch; + w = h->w; + std = (struct gf_wgen_log_w16_data *) h->private; + + std->log = &(std->base); + std->anti = std->log + (1<w); + std->danti = std->anti + (1<w)-1; + + for (i = 0; i < ((uint32_t)1 << w); i++) + std->log[i] = 0; + + a = 1; + for(i=0; i < ((uint32_t)1<log[a] != 0) check = 1; + std->log[a] = i; + std->anti[i] = a; + std->danti[i] = a; + a <<= 1; + if(a & (1<prim_poly; + //a &= ((1 << w)-1); + } + + if (check) { + if (h->mult_type != GF_MULT_LOG_TABLE) return gf_wgen_shift_init(gf); + _gf_errno = GF_E_LOGPOLY; + return 0; + } + + SET_FUNCTION(gf,multiply,w32,gf_wgen_log_16_multiply) + SET_FUNCTION(gf,divide,w32,gf_wgen_log_16_divide) + return 1; +} + +static +gf_val_32_t +gf_wgen_log_32_multiply(gf_t *gf, gf_val_32_t a, gf_val_32_t b) +{ + gf_internal_t *h; + struct gf_wgen_log_w32_data *std; + + h = (gf_internal_t *) gf->scratch; + std = (struct gf_wgen_log_w32_data *) h->private; + + if (a == 0 || b == 0) return 0; + return (std->anti[std->log[a]+std->log[b]]); +} + +static +gf_val_32_t +gf_wgen_log_32_divide(gf_t *gf, gf_val_32_t a, gf_val_32_t b) +{ + gf_internal_t *h; + struct gf_wgen_log_w32_data *std; + int index; + + h = (gf_internal_t *) gf->scratch; + std = (struct gf_wgen_log_w32_data *) h->private; + + if (a == 0 || b == 0) return 0; + index = std->log[a]; + index -= std->log[b]; + + return (std->danti[index]); +} + +static +int gf_wgen_log_32_init(gf_t *gf) +{ + gf_internal_t *h; + struct gf_wgen_log_w32_data *std; + int w; + uint32_t a, i; + int check = 0; + + h = (gf_internal_t *) gf->scratch; + w = h->w; + std = (struct gf_wgen_log_w32_data *) h->private; + + std->log = &(std->base); + std->anti = std->log + (1<w); + std->danti = std->anti + (1<w)-1; + + for (i = 0; i < ((uint32_t)1 << w); i++) + std->log[i] = 0; + + a = 1; + for(i=0; i < ((uint32_t)1<log[a] != 0) check = 1; + std->log[a] = i; + std->anti[i] = a; + std->danti[i] = a; + a <<= 1; + if(a & (1<prim_poly; + //a &= ((1 << w)-1); + } + + if (check != 0) { + _gf_errno = GF_E_LOGPOLY; + return 0; + } + + SET_FUNCTION(gf,multiply,w32,gf_wgen_log_32_multiply) + SET_FUNCTION(gf,divide,w32,gf_wgen_log_32_divide) + return 1; +} + +static +int gf_wgen_log_init(gf_t *gf) +{ + gf_internal_t *h; + + h = (gf_internal_t *) gf->scratch; + if (h->w <= 8) return gf_wgen_log_8_init(gf); + if (h->w <= 16) return gf_wgen_log_16_init(gf); + if (h->w <= 32) return gf_wgen_log_32_init(gf); + + /* Returning zero to make the compiler happy, but this won't get + executed, because it is tested in _scratch_space. */ + + return 0; +} + +int gf_wgen_scratch_size(int w, int mult_type, int region_type, int divide_type, int arg1, int arg2) +{ + + switch(mult_type) + { + case GF_MULT_DEFAULT: + if (w <= 8) { + return sizeof(gf_internal_t) + sizeof(struct gf_wgen_table_w8_data) + + sizeof(uint8_t)*(1 << w)*(1<scratch; + rs = bytes / (h->w); + + written = (xor) ? 0xffffffff : 0; + for (i = 0; i < h->w; i++) { + for (j = 0; j < h->w; j++) { + if (val & (1 << j)) { + gf_multby_one(src, ((uint8_t *)dest) + j*rs, rs, (written & (1 << j))); + written |= (1 << j); + } + } + src = (uint8_t *)src + rs; + val = gf->multiply.w32(gf, val, 2); + } +} + +int gf_wgen_init(gf_t *gf) +{ + gf_internal_t *h; + + h = (gf_internal_t *) gf->scratch; + if (h->prim_poly == 0) { + switch (h->w) { + case 1: h->prim_poly = 1; break; + case 2: h->prim_poly = 7; break; + case 3: h->prim_poly = 013; break; + case 4: h->prim_poly = 023; break; + case 5: h->prim_poly = 045; break; + case 6: h->prim_poly = 0103; break; + case 7: h->prim_poly = 0211; break; + case 8: h->prim_poly = 0435; break; + case 9: h->prim_poly = 01021; break; + case 10: h->prim_poly = 02011; break; + case 11: h->prim_poly = 04005; break; + case 12: h->prim_poly = 010123; break; + case 13: h->prim_poly = 020033; break; + case 14: h->prim_poly = 042103; break; + case 15: h->prim_poly = 0100003; break; + case 16: h->prim_poly = 0210013; break; + case 17: h->prim_poly = 0400011; break; + case 18: h->prim_poly = 01000201; break; + case 19: h->prim_poly = 02000047; break; + case 20: h->prim_poly = 04000011; break; + case 21: h->prim_poly = 010000005; break; + case 22: h->prim_poly = 020000003; break; + case 23: h->prim_poly = 040000041; break; + case 24: h->prim_poly = 0100000207; break; + case 25: h->prim_poly = 0200000011; break; + case 26: h->prim_poly = 0400000107; break; + case 27: h->prim_poly = 01000000047; break; + case 28: h->prim_poly = 02000000011; break; + case 29: h->prim_poly = 04000000005; break; + case 30: h->prim_poly = 010040000007; break; + case 31: h->prim_poly = 020000000011; break; + case 32: h->prim_poly = 00020000007; break; + default: fprintf(stderr, "gf_wgen_init: w not defined yet\n"); exit(1); + } + } else { + if (h->w == 32) { + h->prim_poly &= 0xffffffff; + } else { + h->prim_poly |= (1 << h->w); + if (h->prim_poly & ~((1ULL<<(h->w+1))-1)) return 0; + } + } + + SET_FUNCTION(gf,multiply,w32,NULL) + SET_FUNCTION(gf,divide,w32,NULL) + SET_FUNCTION(gf,inverse,w32,NULL) + SET_FUNCTION(gf,multiply_region,w32,gf_wgen_cauchy_region) + SET_FUNCTION(gf,extract_word,w32,gf_wgen_extract_word) + + switch(h->mult_type) { + case GF_MULT_DEFAULT: + if (h->w <= 8) { + if (gf_wgen_table_init(gf) == 0) return 0; + } else if (h->w <= 16) { + if (gf_wgen_log_init(gf) == 0) return 0; + } else { + if (gf_wgen_bytwo_p_init(gf) == 0) return 0; + } + break; + case GF_MULT_SHIFT: if (gf_wgen_shift_init(gf) == 0) return 0; break; + case GF_MULT_BYTWO_b: if (gf_wgen_bytwo_b_init(gf) == 0) return 0; break; + case GF_MULT_BYTWO_p: if (gf_wgen_bytwo_p_init(gf) == 0) return 0; break; + case GF_MULT_GROUP: if (gf_wgen_group_init(gf) == 0) return 0; break; + case GF_MULT_TABLE: if (gf_wgen_table_init(gf) == 0) return 0; break; + case GF_MULT_LOG_TABLE: if (gf_wgen_log_init(gf) == 0) return 0; break; + default: return 0; + } + if (h->divide_type == GF_DIVIDE_EUCLID) { + SET_FUNCTION(gf,divide,w32,gf_wgen_divide_from_inverse) + SET_FUNCTION(gf,inverse,w32,gf_wgen_euclid) + } else if (h->divide_type == GF_DIVIDE_MATRIX) { + SET_FUNCTION(gf,divide,w32,gf_wgen_divide_from_inverse) + SET_FUNCTION(gf,inverse,w32,gf_wgen_matrix) + } + + if (gf->inverse.w32== NULL && gf->divide.w32 == NULL) SET_FUNCTION(gf,inverse,w32,gf_wgen_euclid) + + if (gf->inverse.w32 != NULL && gf->divide.w32 == NULL) { + SET_FUNCTION(gf,divide,w32,gf_wgen_divide_from_inverse) + } + if (gf->inverse.w32 == NULL && gf->divide.w32 != NULL) { + SET_FUNCTION(gf,inverse,w32,gf_wgen_inverse_from_divide) + } + return 1; +} diff --git a/IDA_new/gf-complete/src/neon/gf_w16_neon.c b/IDA_new/gf-complete/src/neon/gf_w16_neon.c new file mode 100644 index 0000000..477ee63 --- /dev/null +++ b/IDA_new/gf-complete/src/neon/gf_w16_neon.c @@ -0,0 +1,276 @@ +/* + * GF-Complete: A Comprehensive Open Source Library for Galois Field Arithmetic + * James S. Plank, Ethan L. Miller, Kevin M. Greenan, + * Benjamin A. Arnold, John A. Burnum, Adam W. Disney, Allen C. McBride. + * + * Copyright (c) 2014: Janne Grunau + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * - Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * + * - Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in + * the documentation and/or other materials provided with the + * distribution. + * + * - Neither the name of the University of Tennessee nor the names of its + * contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, + * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS + * OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED + * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY + * WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + * + * + * gf_w16_neon.c + * + * Neon routines for 16-bit Galois fields + * + */ + +#include "gf_int.h" +#include +#include +#include "gf_w16.h" + +#ifndef ARCH_AARCH64 +#define vqtbl1q_u8(tbl, v) vcombine_u8(vtbl2_u8(tbl, vget_low_u8(v)), \ + vtbl2_u8(tbl, vget_high_u8(v))) +#endif + +static +inline +void +neon_w16_split_4_multiply_region(gf_t *gf, uint16_t *src, uint16_t *dst, + uint16_t *d_end, uint8_t *tbl, + gf_val_32_t val, int xor) +{ + unsigned i; + uint8_t *high = tbl + 4 * 16; + uint8x16_t loset, rl, rh; + uint8x16x2_t va; + +#ifdef ARCH_AARCH64 + uint8x16_t tbl_h[4], tbl_l[4]; + for (i = 0; i < 4; i++) { + tbl_l[i] = vld1q_u8(tbl + i*16); + tbl_h[i] = vld1q_u8(high + i*16); + } +#else + uint8x8x2_t tbl_h[4], tbl_l[4]; + for (i = 0; i < 4; i++) { + tbl_l[i].val[0] = vld1_u8(tbl + i*16); + tbl_l[i].val[1] = vld1_u8(tbl + i*16 + 8); + tbl_h[i].val[0] = vld1_u8(high + i*16); + tbl_h[i].val[1] = vld1_u8(high + i*16 + 8); + } +#endif + + loset = vdupq_n_u8(0xf); + + if (xor) { + uint8x16x2_t vb; + while (dst < d_end) { + va = vld2q_u8((uint8_t*)src); + vb = vld2q_u8((uint8_t*)dst); + + rl = vqtbl1q_u8(tbl_l[0], vandq_u8(va.val[0], loset)); + rh = vqtbl1q_u8(tbl_h[0], vandq_u8(va.val[0], loset)); + rl = veorq_u8(rl, vqtbl1q_u8(tbl_l[2], vandq_u8(va.val[1], loset))); + rh = veorq_u8(rh, vqtbl1q_u8(tbl_h[2], vandq_u8(va.val[1], loset))); + + va.val[0] = vshrq_n_u8(va.val[0], 4); + va.val[1] = vshrq_n_u8(va.val[1], 4); + + rl = veorq_u8(rl, vqtbl1q_u8(tbl_l[1], va.val[0])); + rh = veorq_u8(rh, vqtbl1q_u8(tbl_h[1], va.val[0])); + va.val[0] = veorq_u8(rl, vqtbl1q_u8(tbl_l[3], va.val[1])); + va.val[1] = veorq_u8(rh, vqtbl1q_u8(tbl_h[3], va.val[1])); + + va.val[0] = veorq_u8(va.val[0], vb.val[0]); + va.val[1] = veorq_u8(va.val[1], vb.val[1]); + vst2q_u8((uint8_t*)dst, va); + + src += 16; + dst += 16; + } + } else { + while (dst < d_end) { + va = vld2q_u8((uint8_t*)src); + + rl = vqtbl1q_u8(tbl_l[0], vandq_u8(va.val[0], loset)); + rh = vqtbl1q_u8(tbl_h[0], vandq_u8(va.val[0], loset)); + rl = veorq_u8(rl, vqtbl1q_u8(tbl_l[2], vandq_u8(va.val[1], loset))); + rh = veorq_u8(rh, vqtbl1q_u8(tbl_h[2], vandq_u8(va.val[1], loset))); + + va.val[0] = vshrq_n_u8(va.val[0], 4); + va.val[1] = vshrq_n_u8(va.val[1], 4); + + rl = veorq_u8(rl, vqtbl1q_u8(tbl_l[1], va.val[0])); + rh = veorq_u8(rh, vqtbl1q_u8(tbl_h[1], va.val[0])); + va.val[0] = veorq_u8(rl, vqtbl1q_u8(tbl_l[3], va.val[1])); + va.val[1] = veorq_u8(rh, vqtbl1q_u8(tbl_h[3], va.val[1])); + + vst2q_u8((uint8_t*)dst, va); + + src += 16; + dst += 16; + } + } +} + +static +inline +void +neon_w16_split_4_altmap_multiply_region(gf_t *gf, uint8_t *src, + uint8_t *dst, uint8_t *d_end, + uint8_t *tbl, gf_val_32_t val, + int xor) +{ + unsigned i; + uint8_t *high = tbl + 4 * 16; + uint8x16_t vh, vl, rh, rl; + uint8x16_t loset; + +#ifdef ARCH_AARCH64 + uint8x16_t tbl_h[4], tbl_l[4]; +#else + uint8x8x2_t tbl_h[4], tbl_l[4]; +#endif + for (i = 0; i < 4; i++) { +#ifdef ARCH_AARCH64 + tbl_l[i] = vld1q_u8(tbl + i*16); + tbl_h[i] = vld1q_u8(high + i*16); +#else + tbl_l[i].val[0] = vld1_u8(tbl + i*16); + tbl_l[i].val[1] = vld1_u8(tbl + i*16 + 8); + tbl_h[i].val[0] = vld1_u8(high + i*16); + tbl_h[i].val[1] = vld1_u8(high + i*16 + 8); +#endif + } + + loset = vdupq_n_u8(0xf); + + while (dst < d_end) { + vh = vld1q_u8(src); + vl = vld1q_u8(src + 16); + + rl = vqtbl1q_u8(tbl_l[0], vandq_u8(vl, loset)); + rh = vqtbl1q_u8(tbl_h[0], vandq_u8(vl, loset)); + rl = veorq_u8(rl, vqtbl1q_u8(tbl_l[2], vandq_u8(vh, loset))); + rh = veorq_u8(rh, vqtbl1q_u8(tbl_h[2], vandq_u8(vh, loset))); + + vl = vshrq_n_u8(vl, 4); + vh = vshrq_n_u8(vh, 4); + + rl = veorq_u8(rl, vqtbl1q_u8(tbl_l[1], vl)); + rh = veorq_u8(rh, vqtbl1q_u8(tbl_h[1], vl)); + rl = veorq_u8(rl, vqtbl1q_u8(tbl_l[3], vh)); + rh = veorq_u8(rh, vqtbl1q_u8(tbl_h[3], vh)); + + if (xor) { + vh = vld1q_u8(dst); + vl = vld1q_u8(dst + 16); + rh = veorq_u8(rh, vh); + rl = veorq_u8(rl, vl); + } + vst1q_u8(dst, rh); + vst1q_u8(dst + 16, rl); + + src += 32; + dst += 32; + } +} + + + +static +inline +void +neon_w16_split_4_16_lazy_multiply_region(gf_t *gf, void *src, void *dest, + gf_val_32_t val, int bytes, int xor, + int altmap) +{ + gf_region_data rd; + unsigned i, j; + uint64_t c, prod; + uint8_t tbl[2 * 4 * 16]; + uint8_t *high = tbl + 4 * 16; + + if (val == 0) { gf_multby_zero(dest, bytes, xor); return; } + if (val == 1) { gf_multby_one(src, dest, bytes, xor); return; } + + for (i = 0; i < 4; i++) { + for (j = 0; j < 16; j++) { + c = (j << (i*4)); + prod = gf->multiply.w32(gf, c, val); + tbl[i*16 + j] = prod & 0xff; + high[i*16 + j] = prod >> 8; + } + } + + gf_set_region_data(&rd, gf, src, dest, bytes, val, xor, 32); + gf_do_initial_region_alignment(&rd); + + if (altmap) { + uint8_t *s8 = rd.s_start; + uint8_t *d8 = rd.d_start; + uint8_t *end8 = rd.d_top; + if (xor) + neon_w16_split_4_altmap_multiply_region(gf, s8, d8, end8, tbl, val, 1); + else + neon_w16_split_4_altmap_multiply_region(gf, s8, d8, end8, tbl, val, 0); + } else { + uint16_t *s16 = rd.s_start; + uint16_t *d16 = rd.d_start; + uint16_t *end16 = rd.d_top; + if (xor) + neon_w16_split_4_multiply_region(gf, s16, d16, end16, tbl, val, 1); + else + neon_w16_split_4_multiply_region(gf, s16, d16, end16, tbl, val, 0); + } + + gf_do_final_region_alignment(&rd); +} + +static +void +gf_w16_split_4_16_lazy_multiply_region_neon(gf_t *gf, void *src, void *dest, + gf_val_32_t val, int bytes, int xor) +{ + neon_w16_split_4_16_lazy_multiply_region(gf, src, dest, val, bytes, xor, 0); +} + +static +void +gf_w16_split_4_16_lazy_altmap_multiply_region_neon(gf_t *gf, void *src, + void *dest, + gf_val_32_t val, int bytes, + int xor) +{ + neon_w16_split_4_16_lazy_multiply_region(gf, src, dest, val, bytes, xor, 1); +} + + +void gf_w16_neon_split_init(gf_t *gf) +{ + gf_internal_t *h = (gf_internal_t *) gf->scratch; + + if (h->region_type & GF_REGION_ALTMAP) + SET_FUNCTION(gf,multiply_region,w32,gf_w16_split_4_16_lazy_altmap_multiply_region_neon) + else + SET_FUNCTION(gf,multiply_region,w32,gf_w16_split_4_16_lazy_multiply_region_neon) +} diff --git a/IDA_new/gf-complete/src/neon/gf_w32_neon.c b/IDA_new/gf-complete/src/neon/gf_w32_neon.c new file mode 100644 index 0000000..7fd1329 --- /dev/null +++ b/IDA_new/gf-complete/src/neon/gf_w32_neon.c @@ -0,0 +1,269 @@ +/* + * GF-Complete: A Comprehensive Open Source Library for Galois Field Arithmetic + * James S. Plank, Ethan L. Miller, Kevin M. Greenan, + * Benjamin A. Arnold, John A. Burnum, Adam W. Disney, Allen C. McBride. + * + * Copyright (c) 2014: Janne Grunau + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * - Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * + * - Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in + * the documentation and/or other materials provided with the + * distribution. + * + * - Neither the name of the University of Tennessee nor the names of its + * contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, + * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS + * OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED + * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY + * WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + * + * gf_w32_neon.c + * + * Neon routines for 32-bit Galois fields + * + */ + + +#include "gf_int.h" +#include +#include +#include "gf_w32.h" + +#ifndef ARCH_AARCH64 +#define vqtbl1q_u8(tbl, v) vcombine_u8(vtbl2_u8(tbl, vget_low_u8(v)), \ + vtbl2_u8(tbl, vget_high_u8(v))) +#endif + +static +void +neon_w32_split_4_32_multiply_region(gf_t *gf, uint32_t *src, uint32_t *dst, + uint32_t *d_end, uint8_t btable[8][4][16], + uint32_t val, int xor, int altmap) +{ + int i, j; +#ifdef ARCH_AARCH64 + uint8x16_t tables[8][4]; +#else + uint8x8x2_t tables[8][4]; +#endif + uint32x4_t v0, v1, v2, v3, s0, s1, s2, s3; + uint8x16_t p0, p1, p2, p3, si, mask1; + uint16x8x2_t r0, r1; + uint8x16x2_t q0, q1; + + for (i = 0; i < 8; i++) { + for (j = 0; j < 4; j++) { +#ifdef ARCH_AARCH64 + tables[i][j] = vld1q_u8(btable[i][j]); +#else + tables[i][j].val[0] = vld1_u8(btable[i][j]); + tables[i][j].val[1] = vld1_u8(btable[i][j] + 8); +#endif + } + } + + mask1 = vdupq_n_u8(0xf); + + while (dst < d_end) { + + v0 = vld1q_u32(src); src += 4; + v1 = vld1q_u32(src); src += 4; + v2 = vld1q_u32(src); src += 4; + v3 = vld1q_u32(src); src += 4; + + if (altmap) { + q0.val[0] = vreinterpretq_u8_u32(v0); + q0.val[1] = vreinterpretq_u8_u32(v1); + q1.val[0] = vreinterpretq_u8_u32(v2); + q1.val[1] = vreinterpretq_u8_u32(v3); + } else { + r0 = vtrnq_u16(vreinterpretq_u16_u32(v0), vreinterpretq_u16_u32(v2)); + r1 = vtrnq_u16(vreinterpretq_u16_u32(v1), vreinterpretq_u16_u32(v3)); + + q0 = vtrnq_u8(vreinterpretq_u8_u16(r0.val[0]), + vreinterpretq_u8_u16(r1.val[0])); + q1 = vtrnq_u8(vreinterpretq_u8_u16(r0.val[1]), + vreinterpretq_u8_u16(r1.val[1])); + } + + si = vandq_u8(q0.val[0], mask1); + p0 = vqtbl1q_u8(tables[0][0], si); + p1 = vqtbl1q_u8(tables[0][1], si); + p2 = vqtbl1q_u8(tables[0][2], si); + p3 = vqtbl1q_u8(tables[0][3], si); + + si = vshrq_n_u8(q0.val[0], 4); + p0 = veorq_u8(p0, vqtbl1q_u8(tables[1][0], si)); + p1 = veorq_u8(p1, vqtbl1q_u8(tables[1][1], si)); + p2 = veorq_u8(p2, vqtbl1q_u8(tables[1][2], si)); + p3 = veorq_u8(p3, vqtbl1q_u8(tables[1][3], si)); + + si = vandq_u8(q0.val[1], mask1); + p0 = veorq_u8(p0, vqtbl1q_u8(tables[2][0], si)); + p1 = veorq_u8(p1, vqtbl1q_u8(tables[2][1], si)); + p2 = veorq_u8(p2, vqtbl1q_u8(tables[2][2], si)); + p3 = veorq_u8(p3, vqtbl1q_u8(tables[2][3], si)); + + si = vshrq_n_u8(q0.val[1], 4); + p0 = veorq_u8(p0, vqtbl1q_u8(tables[3][0], si)); + p1 = veorq_u8(p1, vqtbl1q_u8(tables[3][1], si)); + p2 = veorq_u8(p2, vqtbl1q_u8(tables[3][2], si)); + p3 = veorq_u8(p3, vqtbl1q_u8(tables[3][3], si)); + + si = vandq_u8(q1.val[0], mask1); + p0 = veorq_u8(p0, vqtbl1q_u8(tables[4][0], si)); + p1 = veorq_u8(p1, vqtbl1q_u8(tables[4][1], si)); + p2 = veorq_u8(p2, vqtbl1q_u8(tables[4][2], si)); + p3 = veorq_u8(p3, vqtbl1q_u8(tables[4][3], si)); + + si = vshrq_n_u8(q1.val[0], 4); + p0 = veorq_u8(p0, vqtbl1q_u8(tables[5][0], si)); + p1 = veorq_u8(p1, vqtbl1q_u8(tables[5][1], si)); + p2 = veorq_u8(p2, vqtbl1q_u8(tables[5][2], si)); + p3 = veorq_u8(p3, vqtbl1q_u8(tables[5][3], si)); + + si = vandq_u8(q1.val[1], mask1); + p0 = veorq_u8(p0, vqtbl1q_u8(tables[6][0], si)); + p1 = veorq_u8(p1, vqtbl1q_u8(tables[6][1], si)); + p2 = veorq_u8(p2, vqtbl1q_u8(tables[6][2], si)); + p3 = veorq_u8(p3, vqtbl1q_u8(tables[6][3], si)); + + si = vshrq_n_u8(q1.val[1], 4); + p0 = veorq_u8(p0, vqtbl1q_u8(tables[7][0], si)); + p1 = veorq_u8(p1, vqtbl1q_u8(tables[7][1], si)); + p2 = veorq_u8(p2, vqtbl1q_u8(tables[7][2], si)); + p3 = veorq_u8(p3, vqtbl1q_u8(tables[7][3], si)); + + if (altmap) { + s0 = vreinterpretq_u32_u8(p0); + s1 = vreinterpretq_u32_u8(p1); + s2 = vreinterpretq_u32_u8(p2); + s3 = vreinterpretq_u32_u8(p3); + } else { + q0 = vtrnq_u8(p0, p1); + q1 = vtrnq_u8(p2, p3); + + r0 = vtrnq_u16(vreinterpretq_u16_u8(q0.val[0]), + vreinterpretq_u16_u8(q1.val[0])); + r1 = vtrnq_u16(vreinterpretq_u16_u8(q0.val[1]), + vreinterpretq_u16_u8(q1.val[1])); + + s0 = vreinterpretq_u32_u16(r0.val[0]); + s1 = vreinterpretq_u32_u16(r1.val[0]); + s2 = vreinterpretq_u32_u16(r0.val[1]); + s3 = vreinterpretq_u32_u16(r1.val[1]); + } + + if (xor) { + v0 = vld1q_u32(dst); + v1 = vld1q_u32(dst + 4); + v2 = vld1q_u32(dst + 8); + v3 = vld1q_u32(dst + 12); + s0 = veorq_u32(s0, v0); + s1 = veorq_u32(s1, v1); + s2 = veorq_u32(s2, v2); + s3 = veorq_u32(s3, v3); + } + + vst1q_u32(dst, s0); + vst1q_u32(dst + 4, s1); + vst1q_u32(dst + 8, s2); + vst1q_u32(dst + 12, s3); + + dst += 16; + } +} + +static +inline +void +neon_w32_split_4_32_lazy_multiply_region(gf_t *gf, void *src, void *dest, uint32_t val, int bytes, int xor, int altmap) +{ + gf_internal_t *h; + int i, j, k; + uint32_t pp, v, *s32, *d32, *top, tmp_table[16]; + uint8_t btable[8][4][16]; + gf_region_data rd; + + if (val == 0) { gf_multby_zero(dest, bytes, xor); return; } + if (val == 1) { gf_multby_one(src, dest, bytes, xor); return; } + + h = (gf_internal_t *) gf->scratch; + pp = h->prim_poly; + + gf_set_region_data(&rd, gf, src, dest, bytes, val, xor, 64); + gf_do_initial_region_alignment(&rd); + + s32 = (uint32_t *) rd.s_start; + d32 = (uint32_t *) rd.d_start; + top = (uint32_t *) rd.d_top; + + v = val; + for (i = 0; i < 8; i++) { + tmp_table[0] = 0; + for (j = 1; j < 16; j <<= 1) { + for (k = 0; k < j; k++) { + tmp_table[k^j] = (v ^ tmp_table[k]); + } + v = (v & GF_FIRST_BIT) ? ((v << 1) ^ pp) : (v << 1); + } + for (j = 0; j < 4; j++) { + for (k = 0; k < 16; k++) { + btable[i][j][k] = (uint8_t) tmp_table[k]; + tmp_table[k] >>= 8; + } + } + } + + if (xor) + neon_w32_split_4_32_multiply_region(gf, s32, d32, top, btable, val, 1, altmap); + else + neon_w32_split_4_32_multiply_region(gf, s32, d32, top, btable, val, 0, altmap); + + gf_do_final_region_alignment(&rd); +} + +static +void +gf_w32_split_4_32_lazy_multiply_region_neon(gf_t *gf, void *src, void *dest, + gf_val_32_t val, int bytes, int xor) +{ + neon_w32_split_4_32_lazy_multiply_region(gf, src, dest, val, bytes, xor, 0); +} + +static +void +gf_w32_split_4_32_lazy_altmap_multiply_region_neon(gf_t *gf, void *src, + void *dest, gf_val_32_t val, + int bytes, int xor) +{ + neon_w32_split_4_32_lazy_multiply_region(gf, src, dest, val, bytes, xor, 1); +} + +void gf_w32_neon_split_init(gf_t *gf) +{ + gf_internal_t *h = (gf_internal_t *) gf->scratch; + + if (h->region_type & GF_REGION_ALTMAP) + SET_FUNCTION(gf,multiply_region,w32,gf_w32_split_4_32_lazy_altmap_multiply_region_neon) + else + SET_FUNCTION(gf,multiply_region,w32,gf_w32_split_4_32_lazy_multiply_region_neon) + +} diff --git a/IDA_new/gf-complete/src/neon/gf_w4_neon.c b/IDA_new/gf-complete/src/neon/gf_w4_neon.c new file mode 100644 index 0000000..5f35c86 --- /dev/null +++ b/IDA_new/gf-complete/src/neon/gf_w4_neon.c @@ -0,0 +1,247 @@ +/* + * GF-Complete: A Comprehensive Open Source Library for Galois Field Arithmetic + * James S. Plank, Ethan L. Miller, Kevin M. Greenan, + * Benjamin A. Arnold, John A. Burnum, Adam W. Disney, Allen C. McBride. + * + * Copyright (c) 2014: Janne Grunau + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * - Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * + * - Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in + * the documentation and/or other materials provided with the + * distribution. + * + * - Neither the name of the University of Tennessee nor the names of its + * contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, + * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS + * OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED + * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY + * WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + * + * gf_w4_neon.c + * + * Neon routines for 4-bit Galois fields + * + */ + +#include "gf_int.h" +#include +#include +#include "gf_w4.h" + +static +gf_val_32_t +gf_w4_neon_clm_multiply (gf_t *gf, gf_val_32_t a4, gf_val_32_t b4) +{ + gf_val_32_t rv = 0; + poly8x8_t result, prim_poly; + poly8x8_t a, b, w; + uint8x8_t v; + gf_internal_t * h = gf->scratch; + + a = vdup_n_p8 (a4); + b = vdup_n_p8 (b4); + + prim_poly = vdup_n_p8 ((uint32_t)(h->prim_poly & 0x1fULL)); + + /* Do the initial multiply */ + result = vmul_p8 (a, b); + v = vshr_n_u8 (vreinterpret_u8_p8(result), 4); + w = vmul_p8 (prim_poly, vreinterpret_p8_u8(v)); + result = vreinterpret_p8_u8 (veor_u8 (vreinterpret_u8_p8(result), vreinterpret_u8_p8(w))); + + /* Extracts 32 bit value from result. */ + rv = (gf_val_32_t)vget_lane_u8 (vreinterpret_u8_p8 (result), 0); + + return rv; +} + +static inline void +neon_clm_multiply_region_from_single (gf_t *gf, uint8_t *s8, uint8_t *d8, + gf_val_32_t val, uint8_t *d_end, int xor) +{ + gf_internal_t * h = gf->scratch; + poly8x8_t prim_poly; + poly8x8_t a, w, even, odd; + uint8x8_t b, c, v, mask; + + a = vdup_n_p8 (val); + mask = vdup_n_u8 (0xf); + prim_poly = vdup_n_p8 ((uint8_t)(h->prim_poly & 0x1fULL)); + + while (d8 < d_end) { + b = vld1_u8 (s8); + + even = vreinterpret_p8_u8 (vand_u8 (b, mask)); + odd = vreinterpret_p8_u8 (vshr_n_u8 (b, 4)); + + if (xor) + c = vld1_u8 (d8); + + even = vmul_p8 (a, even); + odd = vmul_p8 (a, odd); + + v = vshr_n_u8 (vreinterpret_u8_p8(even), 4); + w = vmul_p8 (prim_poly, vreinterpret_p8_u8(v)); + even = vreinterpret_p8_u8 (veor_u8 (vreinterpret_u8_p8(even), vreinterpret_u8_p8(w))); + + v = vshr_n_u8 (vreinterpret_u8_p8(odd), 4); + w = vmul_p8 (prim_poly, vreinterpret_p8_u8(v)); + odd = vreinterpret_p8_u8 (veor_u8 (vreinterpret_u8_p8(odd), vreinterpret_u8_p8(w))); + + v = veor_u8 (vreinterpret_u8_p8 (even), vshl_n_u8 (vreinterpret_u8_p8 (odd), 4)); + + if (xor) + v = veor_u8 (c, v); + + vst1_u8 (d8, v); + + d8 += 8; + s8 += 8; + } +} + + +static void +gf_w4_neon_clm_multiply_region_from_single (gf_t *gf, void *src, void *dest, + gf_val_32_t val, int bytes, int xor) +{ + gf_region_data rd; + uint8_t *s8; + uint8_t *d8; + + if (val == 0) { gf_multby_zero(dest, bytes, xor); return; } + if (val == 1) { gf_multby_one(src, dest, bytes, xor); return; } + + gf_set_region_data(&rd, gf, src, dest, bytes, val, xor, 16); + gf_do_initial_region_alignment(&rd); + + s8 = (uint8_t *) rd.s_start; + d8 = (uint8_t *) rd.d_start; + + if (xor) + neon_clm_multiply_region_from_single (gf, s8, d8, val, rd.d_top, 1); + else + neon_clm_multiply_region_from_single (gf, s8, d8, val, rd.d_top, 0); + + gf_do_final_region_alignment(&rd); +} + +#ifndef ARCH_AARCH64 +#define vqtbl1q_u8(tbl, v) vcombine_u8(vtbl2_u8(tbl, vget_low_u8(v)), \ + vtbl2_u8(tbl, vget_high_u8(v))) +#endif + +static +inline +void +w4_single_table_multiply_region_neon(gf_t *gf, uint8_t *src, uint8_t *dst, + uint8_t * d_end, gf_val_32_t val, int xor) +{ + struct gf_single_table_data *std; + uint8_t *base; + uint8x16_t r, va, vh, vl, loset; + +#ifdef ARCH_AARCH64 + uint8x16_t th, tl; +#else + uint8x8x2_t th, tl; +#endif + + std = (struct gf_single_table_data *) ((gf_internal_t *) (gf->scratch))->private; + base = (uint8_t *) std->mult; + base += (val << GF_FIELD_WIDTH); + +#ifdef ARCH_AARCH64 + tl = vld1q_u8 (base); + th = vshlq_n_u8 (tl, 4); +#else + tl.val[0] = vld1_u8 (base); + tl.val[1] = vld1_u8 (base + 8); + th.val[0] = vshl_n_u8 (tl.val[0], 4); + th.val[1] = vshl_n_u8 (tl.val[1], 4); +#endif + + loset = vdupq_n_u8(0xf); + + while (dst < d_end) { + va = vld1q_u8 (src); + + vh = vshrq_n_u8 (va, 4); + vl = vandq_u8 (va, loset); + + if (xor) + va = vld1q_u8 (dst); + + vh = vqtbl1q_u8 (th, vh); + vl = vqtbl1q_u8 (tl, vl); + + r = veorq_u8 (vh, vl); + + if (xor) + r = veorq_u8 (va, r); + + vst1q_u8 (dst, r); + + dst += 16; + src += 16; + } +} + +static +void +gf_w4_single_table_multiply_region_neon(gf_t *gf, void *src, void *dest, + gf_val_32_t val, int bytes, int xor) +{ + gf_region_data rd; + uint8_t *sptr, *dptr, *top; + + if (val == 0) { gf_multby_zero(dest, bytes, xor); return; } + if (val == 1) { gf_multby_one(src, dest, bytes, xor); return; } + + gf_set_region_data(&rd, gf, src, dest, bytes, val, xor, 16); + gf_do_initial_region_alignment(&rd); + + sptr = rd.s_start; + dptr = rd.d_start; + top = rd.d_top; + + if (xor) + w4_single_table_multiply_region_neon(gf, sptr, dptr, top, val, 1); + else + w4_single_table_multiply_region_neon(gf, sptr, dptr, top, val, 0); + + gf_do_final_region_alignment(&rd); + +} + + +int gf_w4_neon_cfm_init(gf_t *gf) +{ + // single clm multiplication probably pointless + SET_FUNCTION(gf,multiply,w32,gf_w4_neon_clm_multiply) + SET_FUNCTION(gf,multiply_region,w32,gf_w4_neon_clm_multiply_region_from_single) + + return 1; +} + +void gf_w4_neon_single_table_init(gf_t *gf) +{ + SET_FUNCTION(gf,multiply_region,w32,gf_w4_single_table_multiply_region_neon) +} diff --git a/IDA_new/gf-complete/src/neon/gf_w64_neon.c b/IDA_new/gf-complete/src/neon/gf_w64_neon.c new file mode 100644 index 0000000..2409823 --- /dev/null +++ b/IDA_new/gf-complete/src/neon/gf_w64_neon.c @@ -0,0 +1,333 @@ +/* + * GF-Complete: A Comprehensive Open Source Library for Galois Field Arithmetic + * James S. Plank, Ethan L. Miller, Kevin M. Greenan, + * Benjamin A. Arnold, John A. Burnum, Adam W. Disney, Allen C. McBride. + * + * Copyright (c) 2014: Janne Grunau + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * - Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * + * - Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in + * the documentation and/or other materials provided with the + * distribution. + * + * - Neither the name of the University of Tennessee nor the names of its + * contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, + * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS + * OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED + * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY + * WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + * + * gf_w64_neon.c + * + * Neon routines for 64-bit Galois fields + * + */ + +#include "gf_int.h" +#include +#include +#include "gf_w64.h" + + +#ifndef ARCH_AARCH64 +#define vqtbl1q_u8(tbl, v) vcombine_u8(vtbl2_u8(tbl, vget_low_u8(v)), \ + vtbl2_u8(tbl, vget_high_u8(v))) +#endif + +static +inline +void +neon_w64_split_4_lazy_altmap_multiply_region(gf_t *gf, uint64_t *src, + uint64_t *dst, uint64_t *d_end, + uint64_t val, int xor) +{ + unsigned i, j, k; + uint8_t btable[16]; +#ifdef ARCH_AARCH64 + uint8x16_t tables[16][8]; +#else + uint8x8x2_t tables[16][8]; +#endif + uint8x16_t p[8], mask1, si; + + gf_internal_t *h = (gf_internal_t *) gf->scratch; + struct gf_split_4_64_lazy_data *ld = (struct gf_split_4_64_lazy_data *) h->private; + + for (i = 0; i < 16; i++) { + for (j = 0; j < 8; j++) { + for (k = 0; k < 16; k++) { + btable[k] = (uint8_t) ld->tables[i][k]; + ld->tables[i][k] >>= 8; + } +#ifdef ARCH_AARCH64 + tables[i][j] = vld1q_u8(btable); +#else + tables[i][j].val[0] = vld1_u8(btable); + tables[i][j].val[1] = vld1_u8(btable + 8); +#endif + } + } + + mask1 = vdupq_n_u8(0xf); + + while (dst < d_end) { + + if (xor) { + for (i = 0; i < 8; i++) + p[i] = vld1q_u8((uint8_t *) (dst + i * 2)); + } else { + for (i = 0; i < 8; i++) + p[i] = vdupq_n_u8(0); + } + + i = 0; + for (k = 0; k < 8; k++) { + uint8x16_t v0 = vld1q_u8((uint8_t *) src); + src += 2; + + si = vandq_u8(v0, mask1); + for (j = 0; j < 8; j++) { + p[j] = veorq_u8(p[j], vqtbl1q_u8(tables[i][j], si)); + } + i++; + si = vshrq_n_u8(v0, 4); + for (j = 0; j < 8; j++) { + p[j] = veorq_u8(p[j], vqtbl1q_u8(tables[i][j], si)); + } + i++; + + } + for (i = 0; i < 8; i++) { + vst1q_u8((uint8_t *) dst, p[i]); + dst += 2; + } + } +} + +static +inline +void +neon_w64_split_4_lazy_multiply_region(gf_t *gf, uint64_t *src, uint64_t *dst, + uint64_t *d_end, uint64_t val, int xor) +{ + unsigned i, j, k; + uint8_t btable[16]; +#ifdef ARCH_AARCH64 + uint8x16_t tables[16][8]; +#else + uint8x8x2_t tables[16][8]; +#endif + uint8x16_t p[8], mask1, si; + uint64x2_t st[8]; + uint32x4x2_t s32[4]; + uint16x8x2_t s16[4]; + uint8x16x2_t s8[4]; + + gf_internal_t *h = (gf_internal_t *) gf->scratch; + struct gf_split_4_64_lazy_data *ld = (struct gf_split_4_64_lazy_data *) h->private; + + for (i = 0; i < 16; i++) { + for (j = 0; j < 8; j++) { + for (k = 0; k < 16; k++) { + btable[k] = (uint8_t) ld->tables[i][k]; + ld->tables[i][k] >>= 8; + } +#ifdef ARCH_AARCH64 + tables[i][j] = vld1q_u8(btable); +#else + tables[i][j].val[0] = vld1_u8(btable); + tables[i][j].val[1] = vld1_u8(btable + 8); +#endif + } + } + + mask1 = vdupq_n_u8(0xf); + + while (dst < d_end) { + + for (k = 0; k < 8; k++) { + st[k] = vld1q_u64(src); + src += 2; + p[k] = vdupq_n_u8(0); + } + + s32[0] = vuzpq_u32(vreinterpretq_u32_u64(st[0]), + vreinterpretq_u32_u64(st[1])); + s32[1] = vuzpq_u32(vreinterpretq_u32_u64(st[2]), + vreinterpretq_u32_u64(st[3])); + s32[2] = vuzpq_u32(vreinterpretq_u32_u64(st[4]), + vreinterpretq_u32_u64(st[5])); + s32[3] = vuzpq_u32(vreinterpretq_u32_u64(st[6]), + vreinterpretq_u32_u64(st[7])); + + s16[0] = vuzpq_u16(vreinterpretq_u16_u32(s32[0].val[0]), + vreinterpretq_u16_u32(s32[1].val[0])); + s16[1] = vuzpq_u16(vreinterpretq_u16_u32(s32[2].val[0]), + vreinterpretq_u16_u32(s32[3].val[0])); + s16[2] = vuzpq_u16(vreinterpretq_u16_u32(s32[0].val[1]), + vreinterpretq_u16_u32(s32[1].val[1])); + s16[3] = vuzpq_u16(vreinterpretq_u16_u32(s32[2].val[1]), + vreinterpretq_u16_u32(s32[3].val[1])); + + s8[0] = vuzpq_u8(vreinterpretq_u8_u16(s16[0].val[0]), + vreinterpretq_u8_u16(s16[1].val[0])); + s8[1] = vuzpq_u8(vreinterpretq_u8_u16(s16[0].val[1]), + vreinterpretq_u8_u16(s16[1].val[1])); + s8[2] = vuzpq_u8(vreinterpretq_u8_u16(s16[2].val[0]), + vreinterpretq_u8_u16(s16[3].val[0])); + s8[3] = vuzpq_u8(vreinterpretq_u8_u16(s16[2].val[1]), + vreinterpretq_u8_u16(s16[3].val[1])); + + i = 0; + for (k = 0; k < 8; k++) { + si = vandq_u8(s8[k >> 1].val[k & 1], mask1); + for (j = 0; j < 8; j++) { + p[j] = veorq_u8(p[j], vqtbl1q_u8(tables[i][j], si)); + } + i++; + si = vshrq_n_u8(s8[k >> 1].val[k & 1], 4); + for (j = 0; j < 8; j++) { + p[j] = veorq_u8(p[j], vqtbl1q_u8(tables[i][j], si)); + } + i++; + } + + s8[0] = vzipq_u8(p[0], p[1]); + s8[1] = vzipq_u8(p[2], p[3]); + s8[2] = vzipq_u8(p[4], p[5]); + s8[3] = vzipq_u8(p[6], p[7]); + + s16[0] = vzipq_u16(vreinterpretq_u16_u8(s8[0].val[0]), + vreinterpretq_u16_u8(s8[1].val[0])); + s16[1] = vzipq_u16(vreinterpretq_u16_u8(s8[2].val[0]), + vreinterpretq_u16_u8(s8[3].val[0])); + s16[2] = vzipq_u16(vreinterpretq_u16_u8(s8[0].val[1]), + vreinterpretq_u16_u8(s8[1].val[1])); + s16[3] = vzipq_u16(vreinterpretq_u16_u8(s8[2].val[1]), + vreinterpretq_u16_u8(s8[3].val[1])); + + s32[0] = vzipq_u32(vreinterpretq_u32_u16(s16[0].val[0]), + vreinterpretq_u32_u16(s16[1].val[0])); + s32[1] = vzipq_u32(vreinterpretq_u32_u16(s16[0].val[1]), + vreinterpretq_u32_u16(s16[1].val[1])); + s32[2] = vzipq_u32(vreinterpretq_u32_u16(s16[2].val[0]), + vreinterpretq_u32_u16(s16[3].val[0])); + s32[3] = vzipq_u32(vreinterpretq_u32_u16(s16[2].val[1]), + vreinterpretq_u32_u16(s16[3].val[1])); + + for (k = 0; k < 8; k ++) { + st[k] = vreinterpretq_u64_u32(s32[k >> 1].val[k & 1]); + } + + if (xor) { + for (i = 0; i < 8; i++) { + uint64x2_t t1 = vld1q_u64(dst); + vst1q_u64(dst, veorq_u64(st[i], t1)); + dst += 2; + } + } else { + for (i = 0; i < 8; i++) { + vst1q_u64(dst, st[i]); + dst += 2; + } + } + + } +} + +static +void +gf_w64_neon_split_4_lazy_multiply_region(gf_t *gf, void *src, void *dest, + uint64_t val, int bytes, int xor, + int altmap) +{ + gf_internal_t *h; + int i, j, k; + uint64_t pp, v, *s64, *d64, *top; + struct gf_split_4_64_lazy_data *ld; + gf_region_data rd; + + if (val == 0) { gf_multby_zero(dest, bytes, xor); return; } + if (val == 1) { gf_multby_one(src, dest, bytes, xor); return; } + + gf_set_region_data(&rd, gf, src, dest, bytes, val, xor, 128); + gf_do_initial_region_alignment(&rd); + + s64 = (uint64_t *) rd.s_start; + d64 = (uint64_t *) rd.d_start; + top = (uint64_t *) rd.d_top; + + h = (gf_internal_t *) gf->scratch; + pp = h->prim_poly; + ld = (struct gf_split_4_64_lazy_data *) h->private; + + v = val; + for (i = 0; i < 16; i++) { + ld->tables[i][0] = 0; + for (j = 1; j < 16; j <<= 1) { + for (k = 0; k < j; k++) { + ld->tables[i][k^j] = (v ^ ld->tables[i][k]); + } + v = (v & GF_FIRST_BIT) ? ((v << 1) ^ pp) : (v << 1); + } + } + + if (altmap) { + if (xor) + neon_w64_split_4_lazy_altmap_multiply_region(gf, s64, d64, top, val, 1); + else + neon_w64_split_4_lazy_altmap_multiply_region(gf, s64, d64, top, val, 0); + } else { + if (xor) + neon_w64_split_4_lazy_multiply_region(gf, s64, d64, top, val, 1); + else + neon_w64_split_4_lazy_multiply_region(gf, s64, d64, top, val, 0); + } + + gf_do_final_region_alignment(&rd); +} + +static +void +gf_w64_split_4_64_lazy_multiply_region_neon(gf_t *gf, void *src, void *dest, + uint64_t val, int bytes, int xor) +{ + gf_w64_neon_split_4_lazy_multiply_region(gf, src, dest, val, bytes, xor, 0); +} + +static +void +gf_w64_split_4_64_lazy_altmap_multiply_region_neon(gf_t *gf, void *src, + void *dest, uint64_t val, + int bytes, int xor) +{ + gf_w64_neon_split_4_lazy_multiply_region(gf, src, dest, val, bytes, xor, 1); +} + +void gf_w64_neon_split_init(gf_t *gf) +{ + gf_internal_t *h = (gf_internal_t *) gf->scratch; + + if (h->region_type & GF_REGION_ALTMAP) + SET_FUNCTION(gf,multiply_region,w64,gf_w64_split_4_64_lazy_altmap_multiply_region_neon) + else + SET_FUNCTION(gf,multiply_region,w64,gf_w64_split_4_64_lazy_multiply_region_neon) + +} diff --git a/IDA_new/gf-complete/src/neon/gf_w8_neon.c b/IDA_new/gf-complete/src/neon/gf_w8_neon.c new file mode 100644 index 0000000..0cce5ba --- /dev/null +++ b/IDA_new/gf-complete/src/neon/gf_w8_neon.c @@ -0,0 +1,302 @@ +/* + * GF-Complete: A Comprehensive Open Source Library for Galois Field Arithmetic + * James S. Plank, Ethan L. Miller, Kevin M. Greenan, + * Benjamin A. Arnold, John A. Burnum, Adam W. Disney, Allen C. McBride. + * + * Copyright (c) 2014: Janne Grunau + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * - Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * + * - Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in + * the documentation and/or other materials provided with the + * distribution. + * + * - Neither the name of the University of Tennessee nor the names of its + * contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, + * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS + * OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED + * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY + * WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + * + * gf_w8_neon.c + * + * Neon optimized routines for 8-bit Galois fields + * + */ + +#include "gf_int.h" +#include "gf_w8.h" +#include +#include + +/* ARM NEON reducing macro for the carry free multiplication + * vmull_p8 is the carryless multiply operation. Here vshrn_n_u16 shifts + * the result to the right by 1 byte. This allows us to multiply + * the prim_poly by the leading bits of the result. We then xor the result + * of that operation back with the result. */ +#define NEON_CFM_REDUCE(v, w, result, prim_poly, initial) \ + do { \ + if (initial) \ + v = vshrn_n_u16 (vreinterpretq_u16_p16(result), 8); \ + else \ + v = veor_u8 (v, vshrn_n_u16 (vreinterpretq_u16_p16(result), 8)); \ + w = vmull_p8 (prim_poly, vreinterpret_p8_u8(v)); \ + result = vreinterpretq_p16_u16 (veorq_u16 (vreinterpretq_u16_p16(result), vreinterpretq_u16_p16(w))); \ + } while (0) + +static +inline +gf_val_32_t +gf_w8_neon_clm_multiply_x (gf_t *gf, gf_val_32_t a8, gf_val_32_t b8, int x) +{ + gf_val_32_t rv = 0; + poly8x8_t a, b; + uint8x8_t v; + poly16x8_t result; + poly8x8_t prim_poly; + poly16x8_t w; + gf_internal_t * h = gf->scratch; + + a = vdup_n_p8 (a8); + b = vdup_n_p8 (b8); + + prim_poly = vdup_n_p8 ((uint32_t)(h->prim_poly & 0x1ffULL)); + + /* Do the initial multiply */ + result = vmull_p8 (a, b); + + /* Ben: Do prim_poly reduction twice. We are guaranteed that we will only + have to do the reduction at most twice, because (w-2)/z == 2. Where + z is equal to the number of zeros after the leading 1 */ + NEON_CFM_REDUCE (v, w, result, prim_poly, 1); + NEON_CFM_REDUCE (v, w, result, prim_poly, 0); + if (x >= 3) { + NEON_CFM_REDUCE (v, w, result, prim_poly, 0); + } + if (x >= 4) { + NEON_CFM_REDUCE (v, w, result, prim_poly, 0); + } + /* Extracts 32 bit value from result. */ + rv = (gf_val_32_t)vget_lane_u8 (vmovn_u16 (vreinterpretq_u16_p16 (result)), 0); + + return rv; +} + +#define CLM_MULTIPLY(x) \ +static gf_val_32_t gf_w8_neon_clm_multiply_ ## x (gf_t *gf, gf_val_32_t a8, gf_val_32_t b8) \ +{\ + return gf_w8_neon_clm_multiply_x (gf, a8, b8, x);\ +} + +CLM_MULTIPLY(2) +CLM_MULTIPLY(3) +CLM_MULTIPLY(4) + +static inline void +neon_clm_multiply_region_from_single_x(gf_t *gf, uint8_t *s8, uint8_t *d8, + gf_val_32_t val, uint8_t *d_end, + int xor, int x) +{ + gf_internal_t * h = gf->scratch; + poly8x8_t a, b; + uint8x8_t c, v; + poly16x8_t result; + poly8x8_t prim_poly; + poly16x8_t w; + + a = vdup_n_p8 (val); + prim_poly = vdup_n_p8 ((uint8_t)(h->prim_poly & 0xffULL)); + + while (d8 < d_end) { + b = vld1_p8 ((poly8_t *) s8); + + if (xor) + c = vld1_u8 (d8); + + result = vmull_p8 (a, b); + + NEON_CFM_REDUCE(v, w, result, prim_poly, 1); + NEON_CFM_REDUCE (v, w, result, prim_poly, 0); + if (x >= 3) { + NEON_CFM_REDUCE (v, w, result, prim_poly, 0); + } + if (x >= 4) { + NEON_CFM_REDUCE (v, w, result, prim_poly, 0); + } + v = vmovn_u16 (vreinterpretq_u16_p16 (result)); + if (xor) + v = veor_u8 (c, v); + + vst1_u8 (d8, v); + + d8 += 8; + s8 += 8; + } +} + +#define CLM_MULT_REGION(x) \ +static void \ +gf_w8_neon_clm_multiply_region_from_single_ ## x (gf_t *gf, void *src, \ + void *dest, \ + gf_val_32_t val, int bytes, \ + int xor) \ +{ \ + gf_region_data rd; \ + uint8_t *s8; \ + uint8_t *d8; \ + \ + if (val == 0) { gf_multby_zero(dest, bytes, xor); return; } \ + if (val == 1) { gf_multby_one(src, dest, bytes, xor); return; } \ + \ + gf_set_region_data(&rd, gf, src, dest, bytes, val, xor, 16); \ + gf_do_initial_region_alignment(&rd); \ + s8 = (uint8_t *) rd.s_start; \ + d8 = (uint8_t *) rd.d_start; \ + \ + if (xor) \ + neon_clm_multiply_region_from_single_x (gf, s8, d8, val, rd.d_top, 1, x); \ + else \ + neon_clm_multiply_region_from_single_x (gf, s8, d8, val, rd.d_top, 0, x);\ + gf_do_final_region_alignment(&rd); \ +} + +CLM_MULT_REGION(2) +CLM_MULT_REGION(3) +CLM_MULT_REGION(4) + + +int gf_w8_neon_cfm_init(gf_t *gf) +{ + gf_internal_t *h; + + h = (gf_internal_t *) gf->scratch; + + if ((0xe0 & h->prim_poly) == 0){ + SET_FUNCTION(gf,multiply,w32,gf_w8_neon_clm_multiply_2) + SET_FUNCTION(gf,multiply_region,w32,gf_w8_neon_clm_multiply_region_from_single_2) + }else if ((0xc0 & h->prim_poly) == 0){ + SET_FUNCTION(gf,multiply,w32,gf_w8_neon_clm_multiply_3) + SET_FUNCTION(gf,multiply_region,w32,gf_w8_neon_clm_multiply_region_from_single_3) + }else if ((0x80 & h->prim_poly) == 0){ + SET_FUNCTION(gf,multiply,w32,gf_w8_neon_clm_multiply_4) + SET_FUNCTION(gf,multiply_region,w32,gf_w8_neon_clm_multiply_region_from_single_4) + }else{ + return 0; + } + return 1; +} + +#ifndef ARCH_AARCH64 +#define vqtbl1q_u8(tbl, v) vcombine_u8(vtbl2_u8(tbl, vget_low_u8(v)), \ + vtbl2_u8(tbl, vget_high_u8(v))) +#endif + +static +void +gf_w8_split_multiply_region_neon(gf_t *gf, void *src, void *dest, gf_val_32_t val, int bytes, int xor) +{ + uint8_t *bh, *bl, *sptr, *dptr; + uint8x16_t r, va, vh, vl, loset; +#ifdef ARCH_AARCH64 + uint8x16_t mth, mtl; +#else + uint8x8x2_t mth, mtl; +#endif + struct gf_w8_half_table_data *htd; + gf_region_data rd; + + if (val == 0) { gf_multby_zero(dest, bytes, xor); return; } + if (val == 1) { gf_multby_one(src, dest, bytes, xor); return; } + + htd = (struct gf_w8_half_table_data *) ((gf_internal_t *) (gf->scratch))->private; + + gf_set_region_data(&rd, gf, src, dest, bytes, val, xor, 16); + gf_do_initial_region_alignment(&rd); + + bh = (uint8_t *) htd->high; + bh += (val << 4); + bl = (uint8_t *) htd->low; + bl += (val << 4); + + sptr = rd.s_start; + dptr = rd.d_start; + +#ifdef ARCH_AARCH64 + mth = vld1q_u8 (bh); + mtl = vld1q_u8 (bl); +#else + mth.val[0] = vld1_u8 (bh); + mtl.val[0] = vld1_u8 (bl); + mth.val[1] = vld1_u8 (bh + 8); + mtl.val[1] = vld1_u8 (bl + 8); +#endif + + loset = vdupq_n_u8(0xf); + + if (xor) { + while (sptr < (uint8_t *) rd.s_top) { + va = vld1q_u8 (sptr); + + vh = vshrq_n_u8 (va, 4); + vl = vandq_u8 (va, loset); + va = vld1q_u8 (dptr); + + vh = vqtbl1q_u8 (mth, vh); + vl = vqtbl1q_u8 (mtl, vl); + + r = veorq_u8 (vh, vl); + + vst1q_u8 (dptr, veorq_u8 (va, r)); + + dptr += 16; + sptr += 16; + } + } else { + while (sptr < (uint8_t *) rd.s_top) { + va = vld1q_u8 (sptr); + + vh = vshrq_n_u8 (va, 4); + vl = vandq_u8 (va, loset); +#ifdef ARCH_AARCH64 + vh = vqtbl1q_u8 (mth, vh); + vl = vqtbl1q_u8 (mtl, vl); +#else + vh = vcombine_u8 (vtbl2_u8 (mth, vget_low_u8 (vh)), + vtbl2_u8 (mth, vget_high_u8 (vh))); + vl = vcombine_u8 (vtbl2_u8 (mtl, vget_low_u8 (vl)), + vtbl2_u8 (mtl, vget_high_u8 (vl))); +#endif + + r = veorq_u8 (vh, vl); + + vst1q_u8(dptr, r); + + dptr += 16; + sptr += 16; + } + } + + gf_do_final_region_alignment(&rd); +} + + +void gf_w8_neon_split_init(gf_t *gf) +{ + SET_FUNCTION(gf,multiply_region,w32,gf_w8_split_multiply_region_neon) +} diff --git a/IDA_new/gf-complete/test/Makefile.am b/IDA_new/gf-complete/test/Makefile.am new file mode 100644 index 0000000..f590ecc --- /dev/null +++ b/IDA_new/gf-complete/test/Makefile.am @@ -0,0 +1,11 @@ +# GF-Complete 'test' AM file + +AM_CPPFLAGS = -I$(top_builddir)/include -I$(top_srcdir)/include +AM_CFLAGS = -O3 -fPIC + +bin_PROGRAMS = gf_unit + +gf_unit_SOURCES = gf_unit.c +#gf_unit_LDFLAGS = -lgf_complete +gf_unit_LDADD = ../src/libgf_complete.la + diff --git a/IDA_new/gf-complete/test/gf_unit.c b/IDA_new/gf-complete/test/gf_unit.c new file mode 100644 index 0000000..db26849 --- /dev/null +++ b/IDA_new/gf-complete/test/gf_unit.c @@ -0,0 +1,458 @@ +/* + * GF-Complete: A Comprehensive Open Source Library for Galois Field Arithmetic + * James S. Plank, Ethan L. Miller, Kevin M. Greenan, + * Benjamin A. Arnold, John A. Burnum, Adam W. Disney, Allen C. McBride. + * + * gf_unit.c + * + * Performs unit testing for gf arithmetic + */ + +#include "config.h" + +#ifdef HAVE_POSIX_MEMALIGN +#ifndef _XOPEN_SOURCE +#define _XOPEN_SOURCE 600 +#endif +#endif + +#include +#include +#include +#include +#include +#include +#include + +#include "gf_complete.h" +#include "gf_int.h" +#include "gf_method.h" +#include "gf_rand.h" +#include "gf_general.h" + +#define REGION_SIZE (16384) +#define RMASK (0x00000000ffffffffLL) +#define LMASK (0xffffffff00000000LL) + +void problem(char *s) +{ + fprintf(stderr, "Unit test failed.\n"); + fprintf(stderr, "%s\n", s); + exit(1); +} + +char *BM = "Bad Method: "; + +void usage(char *s) +{ + fprintf(stderr, "usage: gf_unit w tests seed [method] - does unit testing in GF(2^w)\n"); + fprintf(stderr, "\n"); + fprintf(stderr, "Legal w are: 1 - 32, 64 and 128\n"); + fprintf(stderr, " 128 is hex only (i.e. '128' will be an error - do '128h')\n"); + fprintf(stderr, "\n"); + fprintf(stderr, "Tests may be any combination of:\n"); + fprintf(stderr, " A: All\n"); + fprintf(stderr, " S: Single operations (multiplication/division)\n"); + fprintf(stderr, " R: Region operations\n"); + fprintf(stderr, " V: Verbose Output\n"); + fprintf(stderr, "\n"); + fprintf(stderr, "Use -1 for time(0) as a seed.\n"); + fprintf(stderr, "\n"); + if (s == BM) { + fprintf(stderr, "%s", BM); + gf_error(); + } else if (s != NULL) { + fprintf(stderr, "%s\n", s); + } + exit(1); +} + +void SigHandler(int v) +{ + fprintf(stderr, "Problem: SegFault!\n"); + fflush(stdout); + exit(2); +} + +int main(int argc, char **argv) +{ + signal(SIGSEGV, SigHandler); + + int w, i, verbose, single, region, top; + int s_start, d_start, bytes, xor, alignment_test; + gf_t gf, gf_def; + time_t t0; + gf_internal_t *h; + gf_general_t *a, *b, *c, *d; + uint8_t a8, b8, c8, *mult4 = NULL, *mult8 = NULL; + uint16_t a16, b16, c16, *log16 = NULL, *alog16 = NULL; + char as[50], bs[50], cs[50], ds[50]; + uint32_t mask = 0; + char *ra, *rb, *rc, *rd, *target; + int align; +#ifndef HAVE_POSIX_MEMALIGN + char *malloc_ra, *malloc_rb, *malloc_rc, *malloc_rd; +#endif + + + if (argc < 4) usage(NULL); + + if (sscanf(argv[1], "%d", &w) == 0){ + usage("Bad w\n"); + } + + if (sscanf(argv[3], "%ld", &t0) == 0) usage("Bad seed\n"); + if (t0 == -1) t0 = time(0); + MOA_Seed(t0); + + if (w > 32 && w != 64 && w != 128) usage("Bad w"); + + if (create_gf_from_argv(&gf, w, argc, argv, 4) == 0) { + usage(BM); + } + + printf("Args: "); + for (i = 1; i < argc; i++) { + printf ("%s ", argv[i]); + } + printf("/ size (bytes): %d\n", gf_size(&gf)); + + for (i = 0; i < strlen(argv[2]); i++) { + if (strchr("ASRV", argv[2][i]) == NULL) usage("Bad test\n"); + } + + h = (gf_internal_t *) gf.scratch; + a = (gf_general_t *) malloc(sizeof(gf_general_t)); + b = (gf_general_t *) malloc(sizeof(gf_general_t)); + c = (gf_general_t *) malloc(sizeof(gf_general_t)); + d = (gf_general_t *) malloc(sizeof(gf_general_t)); + +#if HAVE_POSIX_MEMALIGN + if (posix_memalign((void **) &ra, 16, sizeof(char)*REGION_SIZE)) + ra = NULL; + if (posix_memalign((void **) &rb, 16, sizeof(char)*REGION_SIZE)) + rb = NULL; + if (posix_memalign((void **) &rc, 16, sizeof(char)*REGION_SIZE)) + rc = NULL; + if (posix_memalign((void **) &rd, 16, sizeof(char)*REGION_SIZE)) + rd = NULL; +#else + //15 bytes extra to make sure it's 16byte aligned + malloc_ra = (char *) malloc(sizeof(char)*REGION_SIZE+15); + malloc_rb = (char *) malloc(sizeof(char)*REGION_SIZE+15); + malloc_rc = (char *) malloc(sizeof(char)*REGION_SIZE+15); + malloc_rd = (char *) malloc(sizeof(char)*REGION_SIZE+15); + ra = (uint8_t *) (((uintptr_t) malloc_ra + 15) & ~((uintptr_t) 0xf)); + rb = (uint8_t *) (((uintptr_t) malloc_rb + 15) & ~((uintptr_t) 0xf)); + rc = (uint8_t *) (((uintptr_t) malloc_rc + 15) & ~((uintptr_t) 0xf)); + rd = (uint8_t *) (((uintptr_t) malloc_rd + 15) & ~((uintptr_t) 0xf)); +#endif + + if (w <= 32) { + mask = 0; + for (i = 0; i < w; i++) mask |= (1 << i); + } + + verbose = (strchr(argv[2], 'V') != NULL); + single = (strchr(argv[2], 'S') != NULL || strchr(argv[2], 'A') != NULL); + region = (strchr(argv[2], 'R') != NULL || strchr(argv[2], 'A') != NULL); + + if (!gf_init_hard(&gf_def, w, GF_MULT_DEFAULT, GF_REGION_DEFAULT, GF_DIVIDE_DEFAULT, + (h->mult_type != GF_MULT_COMPOSITE) ? h->prim_poly : 0, 0, 0, NULL, NULL)) + problem("No default for this value of w"); + + if (w == 4) { + mult4 = gf_w4_get_mult_table(&gf); + } else if (w == 8) { + mult8 = gf_w8_get_mult_table(&gf); + } else if (w == 16) { + log16 = gf_w16_get_log_table(&gf); + alog16 = gf_w16_get_mult_alog_table(&gf); + } + + if (verbose) printf("Seed: %ld\n", t0); + + if (single) { + + if (gf.multiply.w32 == NULL) problem("No multiplication operation defined."); + if (verbose) { printf("Testing single multiplications/divisions.\n"); fflush(stdout); } + if (w <= 10) { + top = (1 << w)*(1 << w); + } else { + top = 1024*1024; + } + for (i = 0; i < top; i++) { + if (w <= 10) { + a->w32 = i % (1 << w); + b->w32 = (i >> w); + + //Allen: the following conditions were being run 10 times each. That didn't seem like nearly enough to + //me for these special cases, so I converted to doing this mod stuff to easily make the number of times + //run both larger and proportional to the total size of the run. + } else { + switch (i % 32) + { + case 0: + gf_general_set_zero(a, w); + gf_general_set_random(b, w, 1); + break; + case 1: + gf_general_set_random(a, w, 1); + gf_general_set_zero(b, w); + break; + case 2: + gf_general_set_one(a, w); + gf_general_set_random(b, w, 1); + break; + case 3: + gf_general_set_random(a, w, 1); + gf_general_set_one(b, w); + break; + default: + gf_general_set_random(a, w, 1); + gf_general_set_random(b, w, 1); + } + } + + //Allen: the following special cases for w=64 are based on the code below for w=128. + //These w=64 cases are based on Dr. Plank's suggestion because some of the methods for w=64 + //involve splitting it in two. I think they're less likely to give errors than the 128-bit case + //though, because the 128 bit case is always split in two. + //As with w=128, I'm arbitrarily deciding to do this sort of thing with a quarter of the cases + if (w == 64) { + switch (i % 32) + { + case 0: if (!gf_general_is_one(a, w)) a->w64 &= RMASK; break; + case 1: if (!gf_general_is_one(a, w)) a->w64 &= LMASK; break; + case 2: if (!gf_general_is_one(a, w)) a->w64 &= RMASK; if (!gf_general_is_one(b, w)) b->w64 &= RMASK; break; + case 3: if (!gf_general_is_one(a, w)) a->w64 &= RMASK; if (!gf_general_is_one(b, w)) b->w64 &= LMASK; break; + case 4: if (!gf_general_is_one(a, w)) a->w64 &= LMASK; if (!gf_general_is_one(b, w)) b->w64 &= RMASK; break; + case 5: if (!gf_general_is_one(a, w)) a->w64 &= LMASK; if (!gf_general_is_one(b, w)) b->w64 &= LMASK; break; + case 6: if (!gf_general_is_one(b, w)) b->w64 &= RMASK; break; + case 7: if (!gf_general_is_one(b, w)) b->w64 &= LMASK; break; + } + } + + //Allen: for w=128, we have important special cases where one half or the other of the number is all + //zeros. The probability of hitting such a number randomly is 1^-64, so if we don't force these cases + //we'll probably never hit them. This could be implemented more efficiently by changing the set-random + //function for w=128, but I think this is easier to follow. + //I'm arbitrarily deciding to do this sort of thing with a quarter of the cases + if (w == 128) { + switch (i % 32) + { + case 0: if (!gf_general_is_one(a, w)) a->w128[0] = 0; break; + case 1: if (!gf_general_is_one(a, w)) a->w128[1] = 0; break; + case 2: if (!gf_general_is_one(a, w)) a->w128[0] = 0; if (!gf_general_is_one(b, w)) b->w128[0] = 0; break; + case 3: if (!gf_general_is_one(a, w)) a->w128[0] = 0; if (!gf_general_is_one(b, w)) b->w128[1] = 0; break; + case 4: if (!gf_general_is_one(a, w)) a->w128[1] = 0; if (!gf_general_is_one(b, w)) b->w128[0] = 0; break; + case 5: if (!gf_general_is_one(a, w)) a->w128[1] = 0; if (!gf_general_is_one(b, w)) b->w128[1] = 0; break; + case 6: if (!gf_general_is_one(b, w)) b->w128[0] = 0; break; + case 7: if (!gf_general_is_one(b, w)) b->w128[1] = 0; break; + } + } + + gf_general_multiply(&gf, a, b, c); + + /* If w is 4, 8 or 16, then there are inline multiplication/division methods. + Test them here. */ + + if (w == 4 && mult4 != NULL) { + a8 = a->w32; + b8 = b->w32; + c8 = GF_W4_INLINE_MULTDIV(mult4, a8, b8); + if (c8 != c->w32) { + printf("Error in inline multiplication. %d * %d. Inline = %d. Default = %d.\n", + a8, b8, c8, c->w32); + exit(1); + } + } + + if (w == 8 && mult8 != NULL) { + a8 = a->w32; + b8 = b->w32; + c8 = GF_W8_INLINE_MULTDIV(mult8, a8, b8); + if (c8 != c->w32) { + printf("Error in inline multiplication. %d * %d. Inline = %d. Default = %d.\n", + a8, b8, c8, c->w32); + exit(1); + } + } + + if (w == 16 && log16 != NULL) { + a16 = a->w32; + b16 = b->w32; + c16 = GF_W16_INLINE_MULT(log16, alog16, a16, b16); + if (c16 != c->w32) { + printf("Error in inline multiplication. %d * %d. Inline = %d. Default = %d.\n", + a16, b16, c16, c->w32); + printf("%d %d\n", log16[a16], log16[b16]); + top = log16[a16] + log16[b16]; + printf("%d %d\n", top, alog16[top]); + exit(1); + } + } + + /* If this is not composite, then first test against the default: */ + + if (h->mult_type != GF_MULT_COMPOSITE) { + gf_general_multiply(&gf_def, a, b, d); + + if (!gf_general_are_equal(c, d, w)) { + gf_general_val_to_s(a, w, as, 1); + gf_general_val_to_s(b, w, bs, 1); + gf_general_val_to_s(c, w, cs, 1); + gf_general_val_to_s(d, w, ds, 1); + printf("Error in single multiplication (all numbers in hex):\n\n"); + printf(" gf.multiply(gf, %s, %s) = %s\n", as, bs, cs); + printf(" The default gf multiplier returned %s\n", ds); + exit(1); + } + } + + /* Now, we also need to double-check by other means, in case the default is wanky, + and when we're performing composite operations. Start with 0 and 1, where we know + what the result should be. */ + + if (gf_general_is_zero(a, w) || gf_general_is_zero(b, w) || + gf_general_is_one(a, w) || gf_general_is_one(b, w)) { + if (((gf_general_is_zero(a, w) || gf_general_is_zero(b, w)) && !gf_general_is_zero(c, w)) || + (gf_general_is_one(a, w) && !gf_general_are_equal(b, c, w)) || + (gf_general_is_one(b, w) && !gf_general_are_equal(a, c, w))) { + gf_general_val_to_s(a, w, as, 1); + gf_general_val_to_s(b, w, bs, 1); + gf_general_val_to_s(c, w, cs, 1); + printf("Error in single multiplication (all numbers in hex):\n\n"); + printf(" gf.multiply(gf, %s, %s) = %s, which is clearly wrong.\n", as, bs, cs); + exit(1); + } + } + + /* Dumb check to make sure that it's not returning numbers that are too big: */ + + if (w < 32 && (c->w32 & mask) != c->w32) { + gf_general_val_to_s(a, w, as, 1); + gf_general_val_to_s(b, w, bs, 1); + gf_general_val_to_s(c, w, cs, 1); + printf("Error in single multiplication (all numbers in hex):\n\n"); + printf(" gf.multiply.w32(gf, %s, %s) = %s, which is too big.\n", as, bs, cs); + exit(1); + } + + /* Finally, let's check to see that multiplication and division work together */ + + if (!gf_general_is_zero(a, w)) { + gf_general_divide(&gf, c, a, d); + if (!gf_general_are_equal(b, d, w)) { + gf_general_val_to_s(a, w, as, 1); + gf_general_val_to_s(b, w, bs, 1); + gf_general_val_to_s(c, w, cs, 1); + gf_general_val_to_s(d, w, ds, 1); + printf("Error in single multiplication/division (all numbers in hex):\n\n"); + printf(" gf.multiply(gf, %s, %s) = %s, but gf.divide(gf, %s, %s) = %s\n", as, bs, cs, cs, as, ds); + exit(1); + } + } + + } + } + + if (region) { + if (verbose) { printf("Testing region multiplications\n"); fflush(stdout); } + for (i = 0; i < 1024; i++) { + //Allen: changing to a switch thing as with the single ops to make things proportional + switch (i % 32) + { + case 0: + gf_general_set_zero(a, w); + break; + case 1: + gf_general_set_one(a, w); + break; + case 2: + gf_general_set_two(a, w); + break; + default: + gf_general_set_random(a, w, 1); + } + MOA_Fill_Random_Region(ra, REGION_SIZE); + MOA_Fill_Random_Region(rb, REGION_SIZE); + xor = (i/32)%2; + align = w/8; + if (align == 0) align = 1; + if (align > 16) align = 16; + + /* JSP - Cauchy test. When w < 32 & it doesn't equal 4, 8 or 16, the default is + equal to GF_REGION_CAUCHY, even if GF_REGION_CAUCHY is not set. We are testing + three alignments here: + + 1. Anything goes -- no alignment guaranteed. + 2. Perfect alignment. Here src and dest must be aligned wrt each other, + and bytes must be a multiple of 16*w. + 3. Imperfect alignment. Here we'll have src and dest be aligned wrt each + other, but bytes is simply a multiple of w. That means some XOR's will + be aligned, and some won't. + */ + + if ((h->region_type & GF_REGION_CAUCHY) || (w < 32 && w != 4 && w != 8 && w != 16)) { + alignment_test = (i%3); + + s_start = MOA_Random_W(5, 1); + if (alignment_test == 0) { + d_start = MOA_Random_W(5, 1); + } else { + d_start = s_start; + } + + bytes = (d_start > s_start) ? REGION_SIZE - d_start : REGION_SIZE - s_start; + bytes -= MOA_Random_W(5, 1); + if (alignment_test == 1) { + bytes -= (bytes % (w*16)); + } else { + bytes -= (bytes % w); + } + + target = rb; + + /* JSP - Otherwise, we're testing a non-cauchy test, and alignment + must be more strict. We have to make sure that the regions are + aligned wrt each other on 16-byte pointers. */ + + } else { + s_start = MOA_Random_W(5, 1) * align; + d_start = s_start; + bytes = REGION_SIZE - s_start - MOA_Random_W(5, 1); + bytes -= (bytes % align); + + if (h->mult_type == GF_MULT_COMPOSITE && (h->region_type & GF_REGION_ALTMAP)) { + target = rb ; + } else { + target = (i/64)%2 ? rb : ra; + } + } + + memcpy(rc, ra, REGION_SIZE); + memcpy(rd, target, REGION_SIZE); + gf_general_do_region_multiply(&gf, a, ra+s_start, target+d_start, bytes, xor); + gf_general_do_region_check(&gf, a, rc+s_start, rd+d_start, target+d_start, bytes, xor); + } + } + + free(a); + free(b); + free(c); + free(d); +#ifdef HAVE_POSIX_MEMALIGN + free(ra); + free(rb); + free(rc); + free(rd); +#else + free(malloc_ra); + free(malloc_rb); + free(malloc_rc); + free(malloc_rd); +#endif + + return 0; +} diff --git a/IDA_new/gf-complete/tools/Makefile.am b/IDA_new/gf-complete/tools/Makefile.am new file mode 100644 index 0000000..4ca9131 --- /dev/null +++ b/IDA_new/gf-complete/tools/Makefile.am @@ -0,0 +1,56 @@ +# GF-Complete 'tools' AM file + +AM_CPPFLAGS = -I$(top_builddir)/include -I$(top_srcdir)/include +AM_CFLAGS = -O3 -fPIC + +bin_PROGRAMS = gf_mult gf_div gf_add gf_time gf_methods gf_poly gf_inline_time + +gf_mult_SOURCES = gf_mult.c +#gf_mult_LDFLAGS = -lgf_complete +gf_mult_LDADD = ../src/libgf_complete.la + +gf_div_SOURCES = gf_div.c +#gf_div_LDFLAGS = -lgf_complete +gf_div_LDADD = ../src/libgf_complete.la + +gf_add_SOURCES = gf_add.c +#gf_add_LDFLAGS = -lgf_complete +gf_add_LDADD = ../src/libgf_complete.la + +gf_time_SOURCES = gf_time.c +#gf_time_LDFLAGS = -lgf_complete +gf_time_LDADD = ../src/libgf_complete.la + +gf_methods_SOURCES = gf_methods.c +#gf_methods_LDFLAGS = -lgf_complete +gf_methods_LDADD = ../src/libgf_complete.la + +gf_poly_SOURCES = gf_poly.c +#gf_poly_LDFLAGS = -lgf_complete +gf_poly_LDADD = ../src/libgf_complete.la + +gf_inline_time_SOURCES = gf_inline_time.c +#gf_inline_time_LDFLAGS = -lgf_complete +gf_inline_time_LDADD = ../src/libgf_complete.la + +# gf_unit 8 A -1 -m LOG_ZERO_EXT is excluded until http://lab.jerasure.org/jerasure/gf-complete/issues/13 is resolved +if ENABLE_VALGRIND +VALGRIND = | perl -p -e 's|^|../libtool --mode=execute valgrind --quiet --error-exitcode=1 --tool=memcheck | if(!/gf_unit 8 A -1 -m LOG_ZERO_EXT/)' +endif + +# gf_unit tests as generated by gf_methods +gf_unit_w%.sh: gf_methods + ./$^ $(@:gf_unit_w%.sh=%) -A -U ${VALGRIND} > $@ || rm $@ + +TESTS = gf_unit_w128.sh \ + gf_unit_w64.sh \ + gf_unit_w32.sh \ + gf_unit_w16.sh \ + gf_unit_w8.sh \ + gf_unit_w4.sh + +TEST_EXTENSIONS = .sh +SH_LOG_COMPILER = $(SHELL) +AM_SH_LOG_FLAGS = -e + +CLEANFILES = $(TESTS) diff --git a/IDA_new/gf-complete/tools/gf_add.c b/IDA_new/gf-complete/tools/gf_add.c new file mode 100644 index 0000000..28cc12c --- /dev/null +++ b/IDA_new/gf-complete/tools/gf_add.c @@ -0,0 +1,114 @@ +/* + * GF-Complete: A Comprehensive Open Source Library for Galois Field Arithmetic + * James S. Plank, Ethan L. Miller, Kevin M. Greenan, + * Benjamin A. Arnold, John A. Burnum, Adam W. Disney, Allen C. McBride. + * + * gf_add.c + * + * Adds two numbers in gf_2^w + */ + +#include +#include +#include +#include +#include + +void usage(char *s) +{ + fprintf(stderr, "usage: gf_add a b w - does addition of a and b in GF(2^w)\n"); + fprintf(stderr, " If w has an h on the end, treat a, b and the sum as hexadecimal (no 0x required)\n"); + fprintf(stderr, "\n"); + fprintf(stderr, " legal w are: 1-32, 64 and 128\n"); + fprintf(stderr, " 128 is hex only (i.e. '128' will be an error - do '128h')\n"); + + if (s != NULL) fprintf(stderr, "%s", s); + exit(1); +} + +int read_128(char *s, uint64_t *v) +{ + int l, t; + char save; + + l = strlen(s); + if (l > 32) return 0; + + if (l > 16) { + if (sscanf(s + (l-16), "%llx", (long long unsigned int *) &(v[1])) == 0) return 0; + save = s[l-16]; + s[l-16] = '\0'; + t = sscanf(s, "%llx", (long long unsigned int *) &(v[0])); + s[l-16] = save; + return t; + } else { + v[0] = 0; + return sscanf(s, "%llx", (long long unsigned int *)&(v[1])); + } + return 1; +} + +void print_128(uint64_t *v) +{ + if (v[0] > 0) { + printf("%llx", (long long unsigned int) v[0]); + printf("%016llx", (long long unsigned int) v[1]); + } else { + printf("%llx", (long long unsigned int) v[1]); + } + printf("\n"); +} + + +int main(int argc, char **argv) +{ + int hex, w; + uint32_t a, b, c, top; + uint64_t a64, b64, c64; + uint64_t a128[2], b128[2], c128[2]; + char *format; + + if (argc != 4) usage(NULL); + if (sscanf(argv[3], "%d", &w) == 0) usage("Bad w\n"); + + if (w <= 0 || (w > 32 && w != 64 && w != 128)) usage("Bad w"); + + hex = (strchr(argv[3], 'h') != NULL); + + if (!hex && w == 128) usage(NULL); + + if (w <= 32) { + format = (hex) ? "%x" : "%u"; + if (sscanf(argv[1], format, &a) == 0) usage("Bad a\n"); + if (sscanf(argv[2], format, &b) == 0) usage("Bad b\n"); + + if (w < 32) { + top = (w == 31) ? 0x80000000 : (1 << w); + if (w != 32 && a >= top) usage("a is too large\n"); + if (w != 32 && b >= top) usage("b is too large\n"); + } + + c = a ^ b; + printf(format, c); + printf("\n"); + + } else if (w == 64) { + format = (hex) ? "%llx" : "%llu"; + if (sscanf(argv[1], format, &a64) == 0) usage("Bad a\n"); + if (sscanf(argv[2], format, &b64) == 0) usage("Bad b\n"); + c64 = a64 ^ b64; + + printf(format, c64); + printf("\n"); + + } else if (w == 128) { + + if (read_128(argv[1], a128) == 0) usage("Bad a\n"); + if (read_128(argv[2], b128) == 0) usage("Bad b\n"); + c128[0] = a128[0] ^ b128[0]; + c128[1] = a128[1] ^ b128[1]; + + print_128(c128); + } + exit(0); +} diff --git a/IDA_new/gf-complete/tools/gf_div.c b/IDA_new/gf-complete/tools/gf_div.c new file mode 100644 index 0000000..9797f07 --- /dev/null +++ b/IDA_new/gf-complete/tools/gf_div.c @@ -0,0 +1,68 @@ +/* + * GF-Complete: A Comprehensive Open Source Library for Galois Field Arithmetic + * James S. Plank, Ethan L. Miller, Kevin M. Greenan, + * Benjamin A. Arnold, John A. Burnum, Adam W. Disney, Allen C. McBride. + * + * gf_div.c + * + * Multiplies two numbers in gf_2^w + */ + +#include +#include +#include +#include +#include + +#include "gf_complete.h" +#include "gf_method.h" +#include "gf_general.h" + +void usage(int why) +{ + fprintf(stderr, "usage: gf_div a b w [method] - does division of a and b in GF(2^w)\n"); + if (why == 'W') { + fprintf(stderr, "Bad w.\n"); + fprintf(stderr, "Legal w are: 1 - 32, 64 and 128.\n"); + fprintf(stderr, "Append 'h' to w to treat a, b and the quotient as hexadecimal.\n"); + fprintf(stderr, "w=128 is hex only (i.e. '128' will be an error - do '128h')\n"); + } + if (why == 'A') fprintf(stderr, "Bad a\n"); + if (why == 'B') fprintf(stderr, "Bad b\n"); + if (why == 'M') { + fprintf(stderr, "Bad Method Specification: "); + gf_error(); + } + exit(1); +} + +int main(int argc, char **argv) +{ + int hex, w; + gf_t gf; + gf_general_t a, b, c; + char output[50]; + + if (argc < 4) usage(' '); + + if (sscanf(argv[3], "%d", &w) == 0) usage('W'); + if (w <= 0 || (w > 32 && w != 64 && w != 128)) usage('W'); + + hex = (strchr(argv[3], 'h') != NULL); + if (!hex && w == 128) usage('W'); + + if (argc == 4) { + if (gf_init_easy(&gf, w) == 0) usage('M'); + } else { + if (create_gf_from_argv(&gf, w, argc, argv, 4) == 0) usage('M'); + } + + if (!gf_general_s_to_val(&a, w, argv[1], hex)) usage('A'); + if (!gf_general_s_to_val(&b, w, argv[2], hex)) usage('B'); + + gf_general_divide(&gf, &a, &b, &c); + gf_general_val_to_s(&c, w, output, hex); + + printf("%s\n", output); + exit(0); +} diff --git a/IDA_new/gf-complete/tools/gf_inline_time.c b/IDA_new/gf-complete/tools/gf_inline_time.c new file mode 100644 index 0000000..f8119da --- /dev/null +++ b/IDA_new/gf-complete/tools/gf_inline_time.c @@ -0,0 +1,170 @@ +/* + * GF-Complete: A Comprehensive Open Source Library for Galois Field Arithmetic + * James S. Plank, Ethan L. Miller, Kevin M. Greenan, + * Benjamin A. Arnold, John A. Burnum, Adam W. Disney, Allen C. McBride. + * + * gf_inline_time.c + * + * Times inline single multiplication when w = 4, 8 or 16 + */ + +#include +#include +#include +#include +#include +#include + +#include "gf_complete.h" +#include "gf_rand.h" + +void +timer_start (double *t) +{ + struct timeval tv; + + gettimeofday (&tv, NULL); + *t = (double)tv.tv_sec + (double)tv.tv_usec * 1e-6; +} + +double +timer_split (const double *t) +{ + struct timeval tv; + double cur_t; + + gettimeofday (&tv, NULL); + cur_t = (double)tv.tv_sec + (double)tv.tv_usec * 1e-6; + return (cur_t - *t); +} + +void problem(char *s) +{ + fprintf(stderr, "Timing test failed.\n"); + fprintf(stderr, "%s\n", s); + exit(1); +} + +void usage(char *s) +{ + fprintf(stderr, "usage: gf_inline_time w seed #elts iterations - does timing of single multiplies\n"); + fprintf(stderr, "\n"); + fprintf(stderr, "Legal w are: 4, 8 or 16\n"); + fprintf(stderr, "\n"); + fprintf(stderr, "Use -1 for time(0) as a seed.\n"); + fprintf(stderr, "\n"); + if (s != NULL) fprintf(stderr, "%s\n", s); + exit(1); +} + +int main(int argc, char **argv) +{ + int w, j, i, size, iterations; + gf_t gf; + double timer, elapsed, dnum, num; + uint8_t *ra = NULL, *rb = NULL, *mult4, *mult8; + uint16_t *ra16 = NULL, *rb16 = NULL, *log16, *alog16; + time_t t0; + + if (argc != 5) usage(NULL); + if (sscanf(argv[1], "%d", &w) == 0) usage("Bad w\n"); + if (w != 4 && w != 8 && w != 16) usage("Bad w\n"); + if (sscanf(argv[2], "%ld", &t0) == 0) usage("Bad seed\n"); + if (sscanf(argv[3], "%d", &size) == 0) usage("Bad #elts\n"); + if (sscanf(argv[4], "%d", &iterations) == 0) usage("Bad iterations\n"); + if (t0 == -1) t0 = time(0); + MOA_Seed(t0); + + num = size; + + gf_init_easy(&gf, w); + + printf("Seed: %ld\n", t0); + + if (w == 4 || w == 8) { + ra = (uint8_t *) malloc(size); + rb = (uint8_t *) malloc(size); + + if (ra == NULL || rb == NULL) { perror("malloc"); exit(1); } + } else if (w == 16) { + ra16 = (uint16_t *) malloc(size*2); + rb16 = (uint16_t *) malloc(size*2); + + if (ra16 == NULL || rb16 == NULL) { perror("malloc"); exit(1); } + } + + if (w == 4) { + mult4 = gf_w4_get_mult_table(&gf); + if (mult4 == NULL) { + printf("Couldn't get inline multiplication table.\n"); + exit(1); + } + elapsed = 0; + dnum = 0; + for (i = 0; i < iterations; i++) { + for (j = 0; j < size; j++) { + ra[j] = MOA_Random_W(w, 1); + rb[j] = MOA_Random_W(w, 1); + } + timer_start(&timer); + for (j = 0; j < size; j++) { + ra[j] = GF_W4_INLINE_MULTDIV(mult4, ra[j], rb[j]); + } + dnum += num; + elapsed += timer_split(&timer); + } + printf("Inline mult: %10.6lf s Mops: %10.3lf %10.3lf Mega-ops/s\n", + elapsed, dnum/1024.0/1024.0, dnum/1024.0/1024.0/elapsed); + + } else if (w == 8) { + mult8 = gf_w8_get_mult_table(&gf); + if (mult8 == NULL) { + printf("Couldn't get inline multiplication table.\n"); + exit(1); + } + elapsed = 0; + dnum = 0; + for (i = 0; i < iterations; i++) { + for (j = 0; j < size; j++) { + ra[j] = MOA_Random_W(w, 1); + rb[j] = MOA_Random_W(w, 1); + } + timer_start(&timer); + for (j = 0; j < size; j++) { + ra[j] = GF_W8_INLINE_MULTDIV(mult8, ra[j], rb[j]); + } + dnum += num; + elapsed += timer_split(&timer); + } + printf("Inline mult: %10.6lf s Mops: %10.3lf %10.3lf Mega-ops/s\n", + elapsed, dnum/1024.0/1024.0, dnum/1024.0/1024.0/elapsed); + } else if (w == 16) { + log16 = gf_w16_get_log_table(&gf); + alog16 = gf_w16_get_mult_alog_table(&gf); + if (log16 == NULL) { + printf("Couldn't get inline multiplication table.\n"); + exit(1); + } + elapsed = 0; + dnum = 0; + for (i = 0; i < iterations; i++) { + for (j = 0; j < size; j++) { + ra16[j] = MOA_Random_W(w, 1); + rb16[j] = MOA_Random_W(w, 1); + } + timer_start(&timer); + for (j = 0; j < size; j++) { + ra16[j] = GF_W16_INLINE_MULT(log16, alog16, ra16[j], rb16[j]); + } + dnum += num; + elapsed += timer_split(&timer); + } + printf("Inline mult: %10.6lf s Mops: %10.3lf %10.3lf Mega-ops/s\n", + elapsed, dnum/1024.0/1024.0, dnum/1024.0/1024.0/elapsed); + } + free (ra); + free (rb); + free (ra16); + free (rb16); + return 0; +} diff --git a/IDA_new/gf-complete/tools/gf_methods.c b/IDA_new/gf-complete/tools/gf_methods.c new file mode 100644 index 0000000..b016c33 --- /dev/null +++ b/IDA_new/gf-complete/tools/gf_methods.c @@ -0,0 +1,246 @@ +/* + * GF-Complete: A Comprehensive Open Source Library for Galois Field Arithmetic + * James S. Plank, Ethan L. Miller, Kevin M. Greenan, + * Benjamin A. Arnold, John A. Burnum, Adam W. Disney, Allen C. McBride. + * + * gf_methods.c + * + * Lists supported methods (incomplete w.r.t. GROUP and COMPOSITE) + */ + +#include +#include +#include +#include + +#include "gf_complete.h" +#include "gf_method.h" +#include "gf_int.h" + +#define BNMULTS (8) +static char *BMULTS[BNMULTS] = { "CARRY_FREE", "GROUP48", + "TABLE", "LOG", "SPLIT4", "SPLIT8", "SPLIT88", "COMPOSITE" }; +#define NMULTS (17) +static char *MULTS[NMULTS] = { "SHIFT", "CARRY_FREE", "CARRY_FREE_GK", "GROUP44", "GROUP48", "BYTWO_p", "BYTWO_b", + "TABLE", "LOG", "LOG_ZERO", "LOG_ZERO_EXT", "SPLIT2", + "SPLIT4", "SPLIT8", "SPLIT16", "SPLIT88", "COMPOSITE" }; + +/* Make sure CAUCHY is last */ + +#define NREGIONS (7) +static char *REGIONS[NREGIONS] = { "DOUBLE", "QUAD", "LAZY", "SIMD", "NOSIMD", + "ALTMAP", "CAUCHY" }; + +#define BNREGIONS (4) +static char *BREGIONS[BNREGIONS] = { "DOUBLE", "QUAD", "ALTMAP", "CAUCHY" }; + +#define NDIVS (2) +static char *divides[NDIVS] = { "MATRIX", "EUCLID" }; + +void usage(char *s) +{ + fprintf(stderr, "usage: gf_methods w -BADC -LXUMDRB\n"); + fprintf(stderr, "\n"); + fprintf(stderr, " w can be 1-32, 64, 128\n"); + fprintf(stderr, "\n"); + fprintf(stderr, " -B lists basic methods that are useful\n"); + fprintf(stderr, " -A does a nearly exhaustive listing\n"); + fprintf(stderr, " -D adds EUCLID and MATRIX division\n"); + fprintf(stderr, " -C adds CAUCHY when possible\n"); + fprintf(stderr, " Combinations are fine.\n"); + fprintf(stderr, "\n"); + fprintf(stderr, " -L Simply lists methods\n"); + fprintf(stderr, " -X List methods and functions selected (compile with DEBUG_FUNCTIONS)\n"); + fprintf(stderr, " -U Produces calls to gf_unit\n"); + fprintf(stderr, " -M Produces calls to time_tool.sh for single multiplications\n"); + fprintf(stderr, " -D Produces calls to time_tool.sh for single divisions\n"); + fprintf(stderr, " -R Produces calls to time_tool.sh for region multiplications\n"); + fprintf(stderr, " -B Produces calls to time_tool.sh for the fastest region multiplications\n"); + fprintf(stderr, " Cannot combine L, U, T.\n"); + if (s != NULL) { + fprintf(stderr, "\n"); + fprintf(stderr, "%s\n", s); + } + exit(1); +} + +void print_methods(gf_t *gf) +{ +#ifdef DEBUG_FUNCTIONS + gf_internal_t *h = (gf_internal_t*) gf->scratch; + + printf("multiply = %s\n", h->multiply); + printf("divide = %s\n", h->divide); + printf("inverse = %s\n", h->inverse); + printf("multiply_region = %s\n", h->multiply_region); + printf("extract_word = %s\n", h->extract_word); +#endif +} + +int main(int argc, char *argv[]) +{ + int m, r, d, w, i, sa, j, k, reset, ok; + int nregions; + int nmults; + char **regions; + char **mults; + int exhaustive = 0; + int divide = 0; + int cauchy = 0; + int listing; + char *gf_argv[50], *x; + gf_t gf; + char ls[10]; + char * w_str; + + if (argc != 4) usage(NULL); + w = atoi(argv[1]); + ok = (w >= 1 && w <= 32); + if (w == 64) ok = 1; + if (w == 128) ok = 1; + if (!ok) usage("Bad w"); + + if (argv[2][0] != '-' || argv[3][0] != '-' || strlen(argv[2]) == 1 || strlen(argv[3]) != 2) { + usage(NULL); + } + for (i = 1; argv[2][i] != '\0'; i++) { + switch(argv[2][i]) { + case 'B': exhaustive = 0; break; + case 'A': exhaustive = 1; break; + case 'D': divide = 1; break; + case 'C': cauchy = 1; break; + default: usage("Bad -BADC"); + } + } + + if (strchr("LXUMDRB", argv[3][1]) == NULL) { usage("Bad -LXUMDRB"); } + listing = argv[3][1]; + + if (listing == 'U') { + w_str = "../test/gf_unit %d A -1"; + } else if (listing == 'L' || listing == 'X') { + w_str = "w=%d:"; + } else { + w_str = strdup("sh time_tool.sh X %d"); + x = strchr(w_str, 'X'); + *x = listing; + } + + gf_argv[0] = "-"; + if (create_gf_from_argv(&gf, w, 1, gf_argv, 0) > 0) { + printf(w_str, w); + printf(" - \n"); + gf_free(&gf, 1); + } else if (_gf_errno == GF_E_DEFAULT) { + fprintf(stderr, "Unlabeled failed method: w=%d: -\n", 2); + exit(1); + } + + nregions = (exhaustive) ? NREGIONS : BNREGIONS; + if (!cauchy) nregions--; + regions = (exhaustive) ? REGIONS : BREGIONS; + mults = (exhaustive) ? MULTS : BMULTS; + nmults = (exhaustive) ? NMULTS : BNMULTS; + + + for (m = 0; m < nmults; m++) { + sa = 0; + gf_argv[sa++] = "-m"; + if (strcmp(mults[m], "GROUP44") == 0) { + gf_argv[sa++] = "GROUP"; + gf_argv[sa++] = "4"; + gf_argv[sa++] = "4"; + } else if (strcmp(mults[m], "GROUP48") == 0) { + gf_argv[sa++] = "GROUP"; + gf_argv[sa++] = "4"; + gf_argv[sa++] = "8"; + } else if (strcmp(mults[m], "SPLIT2") == 0) { + gf_argv[sa++] = "SPLIT"; + sprintf(ls, "%d", w); + gf_argv[sa++] = ls; + gf_argv[sa++] = "2"; + } else if (strcmp(mults[m], "SPLIT4") == 0) { + gf_argv[sa++] = "SPLIT"; + sprintf(ls, "%d", w); + gf_argv[sa++] = ls; + gf_argv[sa++] = "4"; + } else if (strcmp(mults[m], "SPLIT8") == 0) { + gf_argv[sa++] = "SPLIT"; + sprintf(ls, "%d", w); + gf_argv[sa++] = ls; + gf_argv[sa++] = "8"; + } else if (strcmp(mults[m], "SPLIT16") == 0) { + gf_argv[sa++] = "SPLIT"; + sprintf(ls, "%d", w); + gf_argv[sa++] = ls; + gf_argv[sa++] = "16"; + } else if (strcmp(mults[m], "SPLIT88") == 0) { + gf_argv[sa++] = "SPLIT"; + gf_argv[sa++] = "8"; + gf_argv[sa++] = "8"; + } else if (strcmp(mults[m], "COMPOSITE") == 0) { + gf_argv[sa++] = "COMPOSITE"; + gf_argv[sa++] = "2"; + gf_argv[sa++] = "-"; + } else { + gf_argv[sa++] = mults[m]; + } + reset = sa; + + + for (r = 0; r < (1 << nregions); r++) { + sa = reset; + for (k = 0; k < nregions; k++) { + if (r & (1 << k)) { + gf_argv[sa++] = "-r"; + gf_argv[sa++] = regions[k]; + } + } + gf_argv[sa++] = "-"; + + /* printf("Hmmmm. %s", gf_argv[0]); + for (j = 0; j < sa; j++) printf(" %s", gf_argv[j]); + printf("\n"); */ + + if (create_gf_from_argv(&gf, w, sa, gf_argv, 0) > 0) { + printf(w_str, w); + for (j = 0; j < sa; j++) printf(" %s", gf_argv[j]); + printf("\n"); + if (listing == 'X') + print_methods(&gf); + gf_free(&gf, 1); + } else if (_gf_errno == GF_E_DEFAULT) { + fprintf(stderr, "Unlabeled failed method: w=%d:", w); + for (j = 0; j < sa; j++) fprintf(stderr, " %s", gf_argv[j]); + fprintf(stderr, "\n"); + exit(1); + } + sa--; + if (divide) { + for (d = 0; d < NDIVS; d++) { + gf_argv[sa++] = "-d"; + gf_argv[sa++] = divides[d]; + /* printf("w=%d:", w); + for (j = 0; j < sa; j++) printf(" %s", gf_argv[j]); + printf("\n"); */ + gf_argv[sa++] = "-"; + if (create_gf_from_argv(&gf, w, sa, gf_argv, 0) > 0) { + printf(w_str, w); + for (j = 0; j < sa; j++) printf(" %s", gf_argv[j]); + printf("\n"); + if (listing == 'X') + print_methods(&gf); + gf_free(&gf, 1); + } else if (_gf_errno == GF_E_DEFAULT) { + fprintf(stderr, "Unlabeled failed method: w=%d:", w); + for (j = 0; j < sa; j++) fprintf(stderr, " %s", gf_argv[j]); + fprintf(stderr, "\n"); + exit(1); + } + sa-=3; + } + } + } + } + return 0; +} diff --git a/IDA_new/gf-complete/tools/gf_mult.c b/IDA_new/gf-complete/tools/gf_mult.c new file mode 100644 index 0000000..815bd8b --- /dev/null +++ b/IDA_new/gf-complete/tools/gf_mult.c @@ -0,0 +1,68 @@ +/* + * GF-Complete: A Comprehensive Open Source Library for Galois Field Arithmetic + * James S. Plank, Ethan L. Miller, Kevin M. Greenan, + * Benjamin A. Arnold, John A. Burnum, Adam W. Disney, Allen C. McBride. + * + * gf_mult.c + * + * Multiplies two numbers in gf_2^w + */ + +#include +#include +#include +#include +#include + +#include "gf_complete.h" +#include "gf_method.h" +#include "gf_general.h" + +void usage(int why) +{ + fprintf(stderr, "usage: gf_mult a b w [method] - does multiplication of a and b in GF(2^w)\n"); + if (why == 'W') { + fprintf(stderr, "Bad w.\n"); + fprintf(stderr, "Legal w are: 1 - 32, 64 and 128.\n"); + fprintf(stderr, "Append 'h' to w to treat a, b and the product as hexadecimal.\n"); + fprintf(stderr, "w=128 is hex only (i.e. '128' will be an error - do '128h')\n"); + } + if (why == 'A') fprintf(stderr, "Bad a\n"); + if (why == 'B') fprintf(stderr, "Bad b\n"); + if (why == 'M') { + fprintf(stderr, "Bad Method Specification: "); + gf_error(); + } + exit(1); +} + +int main(int argc, char **argv) +{ + int hex, w; + gf_t gf; + gf_general_t a, b, c; + char output[50]; + + if (argc < 4) usage(' '); + + if (sscanf(argv[3], "%d", &w) == 0) usage('W'); + if (w <= 0 || (w > 32 && w != 64 && w != 128)) usage('W'); + + hex = (strchr(argv[3], 'h') != NULL); + if (!hex && w == 128) usage('W'); + + if (argc == 4) { + if (gf_init_easy(&gf, w) == 0) usage('M'); + } else { + if (create_gf_from_argv(&gf, w, argc, argv, 4) == 0) usage('M'); + } + + if (!gf_general_s_to_val(&a, w, argv[1], hex)) usage('A'); + if (!gf_general_s_to_val(&b, w, argv[2], hex)) usage('B'); + + gf_general_multiply(&gf, &a, &b, &c); + gf_general_val_to_s(&c, w, output, hex); + + printf("%s\n", output); + exit(0); +} diff --git a/IDA_new/gf-complete/tools/gf_poly.c b/IDA_new/gf-complete/tools/gf_poly.c new file mode 100644 index 0000000..b3faf25 --- /dev/null +++ b/IDA_new/gf-complete/tools/gf_poly.c @@ -0,0 +1,275 @@ +/* + * GF-Complete: A Comprehensive Open Source Library for Galois Field Arithmetic + * James S. Plank, Ethan L. Miller, Kevin M. Greenan, + * Benjamin A. Arnold, John A. Burnum, Adam W. Disney, Allen C. McBride. + * + * gf_poly.c - program to help find irreducible polynomials in composite fields, + * using the Ben-Or algorithm. + * + * (This one was written by Jim) + * + * Please see the following paper for a description of the Ben-Or algorithm: + * + * author S. Gao and D. Panario + * title Tests and Constructions of Irreducible Polynomials over Finite Fields + * booktitle Foundations of Computational Mathematics + * year 1997 + * publisher Springer Verlag + * pages 346-361 + * + * The basic technique is this. You have a polynomial f(x) whose coefficients are + * in a base field GF(2^w). The polynomial is of degree n. You need to do the + * following for all i from 1 to n/2: + * + * Construct x^(2^w)^i modulo f. That will be a polynomial of maximum degree n-1 + * with coefficients in GF(2^w). You construct that polynomial by starting with x + * and doubling it w times, each time taking the result modulo f. Then you + * multiply that by itself i times, again each time taking the result modulo f. + * + * When you're done, you need to "subtract" x -- since addition = subtraction = + * XOR, that means XOR x. + * + * Now, find the GCD of that last polynomial and f, using Euclid's algorithm. If + * the GCD is not one, then f is reducible. If it is not reducible for each of + * those i, then it is irreducible. + * + * In this code, I am using a gf_general_t to represent elements of GF(2^w). This + * is so that I can use base fields that are GF(2^64) or GF(2^128). + * + * I have two main procedures. The first is x_to_q_to_i_minus_x, which calculates + * x^(2^w)^i - x, putting the result into a gf_general_t * called retval. + * + * The second is gcd_one, which takes a polynomial of degree n and a second one + * of degree n-1, and uses Euclid's algorithm to decide if their GCD == 1. + * + * These can be made faster (e.g. calculate x^(2^w) once and store it). + */ + +#include "gf_complete.h" +#include "gf_method.h" +#include "gf_general.h" +#include "gf_int.h" +#include +#include +#include +#include + +char *BM = "Bad Method: "; + +void usage(char *s) +{ + fprintf(stderr, "usage: gf_poly w(base-field) method power:coef [ power:coef .. ]\n"); + fprintf(stderr, "\n"); + fprintf(stderr, " use - for the default method.\n"); + fprintf(stderr, " use 0x in front of the coefficient if it's in hex\n"); + fprintf(stderr, " \n"); + fprintf(stderr, " For example, to test whether x^2 + 2x + 1 is irreducible\n"); + fprintf(stderr, " in GF(2^16), the call is:\n"); + fprintf(stderr, " \n"); + fprintf(stderr, " gf_poly 16 - 2:1 1:2 0:1\n"); + fprintf(stderr, " \n"); + fprintf(stderr, " See the user's manual for more information.\n"); + if (s != NULL) { + fprintf(stderr, "\n"); + if (s == BM) { + fprintf(stderr, "%s", s); + gf_error(); + } else { + fprintf(stderr, "%s\n", s); + } + } + exit(1); +} + +int gcd_one(gf_t *gf, int w, int n, gf_general_t *poly, gf_general_t *prod) +{ + gf_general_t *a, *b, zero, factor, p; + int i, j, da, db; + + gf_general_set_zero(&zero, w); + + a = (gf_general_t *) malloc(sizeof(gf_general_t) * n+1); + b = (gf_general_t *) malloc(sizeof(gf_general_t) * n); + for (i = 0; i <= n; i++) gf_general_add(gf, &zero, poly+i, a+i); + for (i = 0; i < n; i++) gf_general_add(gf, &zero, prod+i, b+i); + + da = n; + while (1) { + for (db = n-1; db >= 0 && gf_general_is_zero(b+db, w); db--) ; + if (db < 0) return 0; + if (db == 0) return 1; + for (j = da; j >= db; j--) { + if (!gf_general_is_zero(a+j, w)) { + gf_general_divide(gf, a+j, b+db, &factor); + for (i = 0; i <= db; i++) { + gf_general_multiply(gf, b+i, &factor, &p); + gf_general_add(gf, &p, a+(i+j-db), a+(i+j-db)); + } + } + } + for (i = 0; i < n; i++) { + gf_general_add(gf, a+i, &zero, &p); + gf_general_add(gf, b+i, &zero, a+i); + gf_general_add(gf, &p, &zero, b+i); + } + } + +} + +void x_to_q_to_i_minus_x(gf_t *gf, int w, int n, gf_general_t *poly, int logq, int i, gf_general_t *retval) +{ + gf_general_t x; + gf_general_t *x_to_q; + gf_general_t *product; + gf_general_t p, zero, factor; + int j, k, lq; + + gf_general_set_zero(&zero, w); + product = (gf_general_t *) malloc(sizeof(gf_general_t) * n*2); + x_to_q = (gf_general_t *) malloc(sizeof(gf_general_t) * n); + for (j = 0; j < n; j++) gf_general_set_zero(x_to_q+j, w); + gf_general_set_one(x_to_q+1, w); + + for (lq = 0; lq < logq; lq++) { + for (j = 0; j < n*2; j++) gf_general_set_zero(product+j, w); + for (j = 0; j < n; j++) { + for (k = 0; k < n; k++) { + gf_general_multiply(gf, x_to_q+j, x_to_q+k, &p); + gf_general_add(gf, product+(j+k), &p, product+(j+k)); + } + } + for (j = n*2-1; j >= n; j--) { + if (!gf_general_is_zero(product+j, w)) { + gf_general_add(gf, product+j, &zero, &factor); + for (k = 0; k <= n; k++) { + gf_general_multiply(gf, poly+k, &factor, &p); + gf_general_add(gf, product+(j-n+k), &p, product+(j-n+k)); + } + } + } + for (j = 0; j < n; j++) gf_general_add(gf, product+j, &zero, x_to_q+j); + } + for (j = 0; j < n; j++) gf_general_set_zero(retval+j, w); + gf_general_set_one(retval, w); + + while (i > 0) { + for (j = 0; j < n*2; j++) gf_general_set_zero(product+j, w); + for (j = 0; j < n; j++) { + for (k = 0; k < n; k++) { + gf_general_multiply(gf, x_to_q+j, retval+k, &p); + gf_general_add(gf, product+(j+k), &p, product+(j+k)); + } + } + for (j = n*2-1; j >= n; j--) { + if (!gf_general_is_zero(product+j, w)) { + gf_general_add(gf, product+j, &zero, &factor); + for (k = 0; k <= n; k++) { + gf_general_multiply(gf, poly+k, &factor, &p); + gf_general_add(gf, product+(j-n+k), &p, product+(j-n+k)); + } + } + } + for (j = 0; j < n; j++) gf_general_add(gf, product+j, &zero, retval+j); + i--; + } + + gf_general_set_one(&x, w); + gf_general_add(gf, &x, retval+1, retval+1); + + free(product); + free(x_to_q); +} + +int main(int argc, char **argv) +{ + int w, i, power, n, ap, success; + gf_t gf; + gf_general_t *poly, *prod; + char *string, *ptr; + char buf[100]; + + if (argc < 4) usage(NULL); + + if (sscanf(argv[1], "%d", &w) != 1 || w <= 0) usage("Bad w."); + ap = create_gf_from_argv(&gf, w, argc, argv, 2); + + if (ap == 0) usage(BM); + + if (ap == argc) usage("No powers/coefficients given."); + + n = -1; + for (i = ap; i < argc; i++) { + if (strchr(argv[i], ':') == NULL || sscanf(argv[i], "%d:", &power) != 1) { + string = (char *) malloc(sizeof(char)*(strlen(argv[i]+100))); + sprintf(string, "Argument '%s' not in proper format of power:coefficient\n", argv[i]); + usage(string); + } + if (power < 0) { + usage("Can't have negative powers\n"); + } else { + n = power; + } + } + // in case the for-loop header fails + assert (n >= 0); + + poly = (gf_general_t *) malloc(sizeof(gf_general_t)*(n+1)); + for (i = 0; i <= n; i++) gf_general_set_zero(poly+i, w); + prod = (gf_general_t *) malloc(sizeof(gf_general_t)*n); + + for (i = ap; i < argc; i++) { + sscanf(argv[i], "%d:", &power); + ptr = strchr(argv[i], ':'); + ptr++; + if (strncmp(ptr, "0x", 2) == 0) { + success = gf_general_s_to_val(poly+power, w, ptr+2, 1); + } else { + success = gf_general_s_to_val(poly+power, w, ptr, 0); + } + if (success == 0) { + string = (char *) malloc(sizeof(char)*(strlen(argv[i]+100))); + sprintf(string, "Argument '%s' not in proper format of power:coefficient\n", argv[i]); + usage(string); + } + } + + printf("Poly:"); + for (power = n; power >= 0; power--) { + if (!gf_general_is_zero(poly+power, w)) { + printf("%s", (power == n) ? " " : " + "); + if (!gf_general_is_one(poly+power, w)) { + gf_general_val_to_s(poly+power, w, buf, 1); + if (n > 0) { + printf("(0x%s)", buf); + } else { + printf("0x%s", buf); + } + } + if (power == 0) { + if (gf_general_is_one(poly+power, w)) printf("1"); + } else if (power == 1) { + printf("x"); + } else { + printf("x^%d", power); + } + } + } + printf("\n"); + + if (!gf_general_is_one(poly+n, w)) { + printf("\n"); + printf("Can't do Ben-Or, because the polynomial is not monic.\n"); + exit(0); + } + + for (i = 1; i <= n/2; i++) { + x_to_q_to_i_minus_x(&gf, w, n, poly, w, i, prod); + if (!gcd_one(&gf, w, n, poly, prod)) { + printf("Reducible.\n"); + exit(0); + } + } + + printf("Irreducible.\n"); + exit(0); +} diff --git a/IDA_new/gf-complete/tools/gf_time.c b/IDA_new/gf-complete/tools/gf_time.c new file mode 100644 index 0000000..7402ab5 --- /dev/null +++ b/IDA_new/gf-complete/tools/gf_time.c @@ -0,0 +1,232 @@ +/* + * GF-Complete: A Comprehensive Open Source Library for Galois Field Arithmetic + * James S. Plank, Ethan L. Miller, Kevin M. Greenan, + * Benjamin A. Arnold, John A. Burnum, Adam W. Disney, Allen C. McBride. + * + * gf_time.c + * + * Performs timing for gf arithmetic + */ + +#include "config.h" + +#ifdef HAVE_POSIX_MEMALIGN +#ifndef _XOPEN_SOURCE +#define _XOPEN_SOURCE 600 +#endif +#endif + +#include +#include +#include +#include +#include +#include + +#include "gf_complete.h" +#include "gf_method.h" +#include "gf_rand.h" +#include "gf_general.h" + +void +timer_start (double *t) +{ + struct timeval tv; + + gettimeofday (&tv, NULL); + *t = (double)tv.tv_sec + (double)tv.tv_usec * 1e-6; +} + +double +timer_split (const double *t) +{ + struct timeval tv; + double cur_t; + + gettimeofday (&tv, NULL); + cur_t = (double)tv.tv_sec + (double)tv.tv_usec * 1e-6; + return (cur_t - *t); +} + +void problem(char *s) +{ + fprintf(stderr, "Timing test failed.\n"); + fprintf(stderr, "%s\n", s); + exit(1); +} + +char *BM = "Bad Method: "; + +void usage(char *s) +{ + fprintf(stderr, "usage: gf_time w tests seed size(bytes) iterations [method [params]] - does timing\n"); + fprintf(stderr, "\n"); + fprintf(stderr, "does unit testing in GF(2^w)\n"); + fprintf(stderr, "\n"); + fprintf(stderr, "Legal w are: 1 - 32, 64 and 128\n"); + fprintf(stderr, "\n"); + fprintf(stderr, "Tests may be any combination of:\n"); + fprintf(stderr, " A: All\n"); + fprintf(stderr, " S: All Single Operations\n"); + fprintf(stderr, " R: All Region Operations\n"); + fprintf(stderr, " M: Single: Multiplications\n"); + fprintf(stderr, " D: Single: Divisions\n"); + fprintf(stderr, " I: Single: Inverses\n"); + fprintf(stderr, " G: Region: Buffer-Constant Multiplication\n"); + fprintf(stderr, " 0: Region: Doing nothing, and bzero()\n"); + fprintf(stderr, " 1: Region: Memcpy() and XOR\n"); + fprintf(stderr, " 2: Region: Multiplying by two\n"); + fprintf(stderr, "\n"); + fprintf(stderr, "Use -1 for time(0) as a seed.\n"); + fprintf(stderr, "\n"); + if (s == BM) { + fprintf(stderr, "%s", BM); + gf_error(); + } else if (s != NULL) { + fprintf(stderr, "%s\n", s); + } + exit(1); +} + +int main(int argc, char **argv) +{ + int w, it, i, size, iterations, xor; + char tests[100]; + char test; + char *single_tests = "MDI"; + char *region_tests = "G012"; + char *tstrings[256]; + void *tmethods[256]; + gf_t gf; + double timer, elapsed, ds, di, dnum; + int num; + time_t t0; + uint8_t *ra, *rb; + gf_general_t a; +#ifndef HAVE_POSIX_MEMALIGN + uint8_t *malloc_ra, *malloc_rb; +#endif + + + if (argc < 6) usage(NULL); + + if (sscanf(argv[1], "%d", &w) == 0){ + usage("Bad w[-pp]\n"); + } + + + if (sscanf(argv[3], "%ld", &t0) == 0) usage("Bad seed\n"); + if (sscanf(argv[4], "%d", &size) == 0) usage("Bad size\n"); + if (sscanf(argv[5], "%d", &iterations) == 0) usage("Bad iterations\n"); + if (t0 == -1) t0 = time(0); + MOA_Seed(t0); + + ds = size; + di = iterations; + + if ((w > 32 && w != 64 && w != 128) || w < 0) usage("Bad w"); + if ((size * 8) % w != 0) usage ("Bad size -- must be a multiple of w*8\n"); + + if (!create_gf_from_argv(&gf, w, argc, argv, 6)) usage(BM); + + strcpy(tests, ""); + for (i = 0; argv[2][i] != '\0'; i++) { + switch(argv[2][i]) { + case 'A': strcat(tests, single_tests); + strcat(tests, region_tests); + break; + case 'S': strcat(tests, single_tests); break; + case 'R': strcat(tests, region_tests); break; + case 'G': strcat(tests, "G"); break; + case '0': strcat(tests, "0"); break; + case '1': strcat(tests, "1"); break; + case '2': strcat(tests, "2"); break; + case 'M': strcat(tests, "M"); break; + case 'D': strcat(tests, "D"); break; + case 'I': strcat(tests, "I"); break; + default: usage("Bad tests"); + } + } + + tstrings['M'] = "Multiply"; + tstrings['D'] = "Divide"; + tstrings['I'] = "Inverse"; + tstrings['G'] = "Region-Random"; + tstrings['0'] = "Region-By-Zero"; + tstrings['1'] = "Region-By-One"; + tstrings['2'] = "Region-By-Two"; + + tmethods['M'] = (void *) gf.multiply.w32; + tmethods['D'] = (void *) gf.divide.w32; + tmethods['I'] = (void *) gf.inverse.w32; + tmethods['G'] = (void *) gf.multiply_region.w32; + tmethods['0'] = (void *) gf.multiply_region.w32; + tmethods['1'] = (void *) gf.multiply_region.w32; + tmethods['2'] = (void *) gf.multiply_region.w32; + + printf("Seed: %ld\n", t0); + +#ifdef HAVE_POSIX_MEMALIGN + if (posix_memalign((void **) &ra, 16, size)) + ra = NULL; + if (posix_memalign((void **) &rb, 16, size)) + rb = NULL; +#else + malloc_ra = (uint8_t *) malloc(size + 15); + malloc_rb = (uint8_t *) malloc(size + 15); + ra = (uint8_t *) (((uintptr_t) malloc_ra + 15) & ~((uintptr_t) 0xf)); + rb = (uint8_t *) (((uintptr_t) malloc_rb + 15) & ~((uintptr_t) 0xf)); +#endif + + if (ra == NULL || rb == NULL) { perror("malloc"); exit(1); } + + for (i = 0; i < 3; i++) { + test = single_tests[i]; + if (strchr(tests, test) != NULL) { + if (tmethods[(int)test] == NULL) { + printf("No %s method.\n", tstrings[(int)test]); + } else { + elapsed = 0; + dnum = 0; + for (it = 0; it < iterations; it++) { + gf_general_set_up_single_timing_test(w, ra, rb, size); + timer_start(&timer); + num = gf_general_do_single_timing_test(&gf, ra, rb, size, test); + dnum += num; + elapsed += timer_split(&timer); + } + printf("%14s: %10.6lf s Mops: %10.3lf %10.3lf Mega-ops/s\n", + tstrings[(int)test], elapsed, + dnum/1024.0/1024.0, dnum/1024.0/1024.0/elapsed); + } + } + } + + for (i = 0; i < 4; i++) { + test = region_tests[i]; + if (strchr(tests, test) != NULL) { + if (tmethods[(int)test] == NULL) { + printf("No %s method.\n", tstrings[(int)test]); + } else { + if (test == '0') gf_general_set_zero(&a, w); + if (test == '1') gf_general_set_one(&a, w); + if (test == '2') gf_general_set_two(&a, w); + + for (xor = 0; xor < 2; xor++) { + elapsed = 0; + for (it = 0; it < iterations; it++) { + if (test == 'G') gf_general_set_random(&a, w, 1); + gf_general_set_up_single_timing_test(8, ra, rb, size); + timer_start(&timer); + gf_general_do_region_multiply(&gf, &a, ra, rb, size, xor); + elapsed += timer_split(&timer); + } + printf("%14s: XOR: %d %10.6lf s MB: %10.3lf %10.3lf MB/s\n", + tstrings[(int)test], xor, elapsed, + ds*di/1024.0/1024.0, ds*di/1024.0/1024.0/elapsed); + } + } + } + } + return 0; +} diff --git a/IDA_new/gf-complete/tools/test_simd.sh b/IDA_new/gf-complete/tools/test_simd.sh new file mode 100755 index 0000000..e514e4f --- /dev/null +++ b/IDA_new/gf-complete/tools/test_simd.sh @@ -0,0 +1,367 @@ +#!/bin/bash -e + +# this scripts has a number of tests for SIMD. It can be invoked +# on the host or on a QEMU machine. + +script_dir="$( cd "$( dirname "${BASH_SOURCE[0]}" )" && pwd )" +host_cpu=`uname -p` +results=${script_dir}/test_simd.results +nprocs=$(grep -c ^processor /proc/cpuinfo) + +# runs unit tests and save the results +test_unit(){ + { ./configure && make clean && make; } || { echo "Compile FAILED" >> ${results}; return 1; } + make -j$nprocs check || { echo "gf_methods $i FAILED" >> ${results}; ((++failed)); } + cat tools/test-suite.log >> ${results} || true +} + +# build with DEBUG_FUNCTIONS and save all methods selected +# to a results file +test_functions() { + failed=0 + + { ./configure --enable-debug-func && make clean && make; } || { echo "Compile FAILED" >> ${results}; return 1; } + for i in 128 64 32 16 8 4; do + { ${script_dir}/gf_methods $i -ACD -X >> ${results}; } || { echo "gf_methods $i FAILED" >> ${results}; ((++failed)); } + done + + return ${failed} +} + +# build with DEBUG_CPU_FUNCTIONS and print out CPU detection +test_detection() { + failed=0 + + { ./configure --enable-debug-cpu && make clean && make; } || { echo "Compile FAILED" >> ${results}; return 1; } + { ${script_dir}/gf_methods 32 -ACD -L | grep '#' >> ${results}; } || { echo "gf_methods $i FAILED" >> ${results}; ((++failed)); } + + return ${failed} +} + +compile_arm() { + failed=0 + + echo -n "Compiling with NO SIMD support..." >> ${results} + { ./configure --disable-neon && make clean && make && echo "SUCCESS" >> ${results}; } || { echo "FAIL" >> ${results}; ((++failed)); } + + echo -n "Compiling with FULL SIMD support..." >> ${results} + { ./configure && make clean && make && echo "SUCCESS" >> ${results}; } || { echo "FAIL" >> ${results}; ((++failed)); } + + return ${failed} +} + +compile_intel() { + failed=0 + + echo -n "Compiling with NO SIMD support..." >> ${results} + { ./configure && make clean && make && echo "SUCCESS" >> ${results}; } || { echo "FAIL" >> ${results}; ((++failed)); } + + echo -n "Compiling with SSE2 only..." >> ${results} + export ax_cv_have_sse_ext=no + export ax_cv_have_sse2_ext=yes + export ax_cv_have_sse3_ext=no + export ax_cv_have_ssse3_ext=no + export ax_cv_have_sse41_ext=no + export ax_cv_have_sse42_ext=no + export ax_cv_have_pclmuldq_ext=no + { ./configure && make clean && make && echo "SUCCESS" >> ${results}; } || { echo "FAIL" >> ${results}; ((++failed)); } + + echo -n "Compiling with SSE2,SSE3 only..." >> ${results} + export ax_cv_have_sse_ext=no + export ax_cv_have_sse2_ext=yes + export ax_cv_have_sse3_ext=yes + export ax_cv_have_ssse3_ext=no + export ax_cv_have_sse41_ext=no + export ax_cv_have_sse42_ext=no + export ax_cv_have_pclmuldq_ext=no + { ./configure && make clean && make && echo "SUCCESS" >> ${results}; } || { echo "FAIL" >> ${results}; ((++failed)); } + + echo -n "Compiling with SSE2,SSE3,SSSE3 only..." >> ${results} + export ax_cv_have_sse_ext=no + export ax_cv_have_sse2_ext=yes + export ax_cv_have_sse3_ext=yes + export ax_cv_have_ssse3_ext=yes + export ax_cv_have_sse41_ext=no + export ax_cv_have_sse42_ext=no + export ax_cv_have_pclmuldq_ext=no + { ./configure && make clean && make && echo "SUCCESS" >> ${results}; } || { echo "FAIL" >> ${results}; ((++failed)); } + + echo -n "Compiling with SSE2,SSE3,SSSE3,SSE4_1 only..." >> ${results} + export ax_cv_have_sse_ext=no + export ax_cv_have_sse2_ext=yes + export ax_cv_have_sse3_ext=yes + export ax_cv_have_ssse3_ext=yes + export ax_cv_have_sse41_ext=yes + export ax_cv_have_sse42_ext=no + export ax_cv_have_pclmuldq_ext=no + { ./configure && make clean && make && echo "SUCCESS" >> ${results}; } || { echo "FAIL" >> ${results}; ((++failed)); } + + echo -n "Compiling with SSE2,SSE3,SSSE3,SSE4_2 only..." >> ${results} + export ax_cv_have_sse_ext=no + export ax_cv_have_sse2_ext=yes + export ax_cv_have_sse3_ext=yes + export ax_cv_have_ssse3_ext=yes + export ax_cv_have_sse41_ext=no + export ax_cv_have_sse42_ext=yes + export ax_cv_have_pclmuldq_ext=no + { ./configure && make clean && make && echo "SUCCESS" >> ${results}; } || { echo "FAIL" >> ${results}; ((++failed)); } + + echo -n "Compiling with FULL SIMD support..." >> ${results} + export ax_cv_have_sse_ext=no + export ax_cv_have_sse2_ext=yes + export ax_cv_have_sse3_ext=yes + export ax_cv_have_ssse3_ext=yes + export ax_cv_have_sse41_ext=yes + export ax_cv_have_sse42_ext=yes + export ax_cv_have_pclmuldq_ext=yes + { ./configure && make clean && make && echo "SUCCESS" >> ${results}; } || { echo "FAIL" >> ${results}; ((++failed)); } + + return ${failed} +} + +# test that we can compile the source code with different +# SIMD options. We assume that we are running on processor +# full SIMD support +test_compile() { + case $host_cpu in + aarch64*|arm*) compile_arm ;; + i[[3456]]86*|x86_64*|amd64*) compile_intel ;; + esac +} + +# disable through build flags +runtime_arm_flags() { + failed=0 + + echo "====NO SIMD support..." >> ${1} + { ./configure --disable-neon --enable-debug-func && make clean && make; } || { echo "Compile FAILED" >> ${1}; return 1; } + for i in 128 64 32 16 8 4; do + { ${script_dir}/gf_methods $i -ACD -X >> ${1}; } || { echo "gf_methods $i FAILED" >> ${1}; ((++failed)); } + done + + echo "====FULL SIMD support..." >> ${1} + { ./configure --enable-debug-func && make clean && make; } || { echo "Compile FAILED" >> ${1}; return 1; } + for i in 128 64 32 16 8 4; do + { ${script_dir}/gf_methods $i -ACD -X >> ${1}; } || { echo "gf_methods $i FAILED" >> ${1}; ((++failed)); } + done + + return ${failed} +} + +# build once with FULL SIMD and disable at runtime through environment +runtime_arm_env() { + failed=0 + + { ./configure --enable-debug-func && make clean && make; } || { echo "Compile FAILED" >> ${1}; return 1; } + + echo "====NO SIMD support..." >> ${1} + export GF_COMPLETE_DISABLE_NEON=1 + for i in 128 64 32 16 8 4; do + { ${script_dir}/gf_methods $i -ACD -X >> ${1}; } || { echo "gf_methods $i FAILED" >> ${1}; ((++failed)); } + done + + echo "====FULL SIMD support..." >> ${1} + unset GF_COMPLETE_DISABLE_NEON + for i in 128 64 32 16 8 4; do + { ${script_dir}/gf_methods $i -ACD -X >> ${1}; } || { echo "gf_methods $i FAILED" >> ${1}; ((++failed)); } + done + + return ${failed} +} + +runtime_intel_flags() { + failed=0 + + echo "====NO SIMD support..." >> ${1} + { ./configure --disable-sse --enable-debug-func && make clean && make; } || { echo "FAIL" >> ${1}; ((++failed)); } + for i in 128 64 32 16 8 4; do + { ${script_dir}/gf_methods $i -ACD -X >> ${1}; } || { echo "gf_methods $i FAILED" >> ${1}; ((++failed)); } + done + + echo "====SSE2 support..." >> ${1} + export ax_cv_have_sse_ext=no + export ax_cv_have_sse2_ext=yes + export ax_cv_have_sse3_ext=no + export ax_cv_have_ssse3_ext=no + export ax_cv_have_sse41_ext=no + export ax_cv_have_sse42_ext=no + export ax_cv_have_pclmuldq_ext=no + { ./configure --enable-debug-func && make clean && make; } || { echo "FAIL" >> ${1}; ((++failed)); } + for i in 128 64 32 16 8 4; do + { ${script_dir}/gf_methods $i -ACD -X >> ${1}; } || { echo "gf_methods $i FAILED" >> ${1}; ((++failed)); } + done + + echo "====SSE2,SSE3 support..." >> ${1} + export ax_cv_have_sse_ext=no + export ax_cv_have_sse2_ext=yes + export ax_cv_have_sse3_ext=yes + export ax_cv_have_ssse3_ext=no + export ax_cv_have_sse41_ext=no + export ax_cv_have_sse42_ext=no + export ax_cv_have_pclmuldq_ext=no + { ./configure --enable-debug-func && make clean && make; } || { echo "FAIL" >> ${1}; ((++failed)); } + for i in 128 64 32 16 8 4; do + { ${script_dir}/gf_methods $i -ACD -X >> ${1}; } || { echo "gf_methods $i FAILED" >> ${1}; ((++failed)); } + done + + echo "====SSE2,SSE3,SSSE3 support..." >> ${1} + export ax_cv_have_sse_ext=no + export ax_cv_have_sse2_ext=yes + export ax_cv_have_sse3_ext=yes + export ax_cv_have_ssse3_ext=yes + export ax_cv_have_sse41_ext=no + export ax_cv_have_sse42_ext=no + export ax_cv_have_pclmuldq_ext=no + { ./configure --enable-debug-func && make clean && make; } || { echo "FAIL" >> ${1}; ((++failed)); } + for i in 128 64 32 16 8 4; do + { ${script_dir}/gf_methods $i -ACD -X >> ${1}; } || { echo "gf_methods $i FAILED" >> ${1}; ((++failed)); } + done + + echo "====SSE2,SSE3,SSSE3,SSE4_1 support..." >> ${1} + export ax_cv_have_sse_ext=no + export ax_cv_have_sse2_ext=yes + export ax_cv_have_sse3_ext=yes + export ax_cv_have_ssse3_ext=yes + export ax_cv_have_sse41_ext=yes + export ax_cv_have_sse42_ext=no + export ax_cv_have_pclmuldq_ext=no + { ./configure --enable-debug-func && make clean && make; } || { echo "FAIL" >> ${1}; ((++failed)); } + for i in 128 64 32 16 8 4; do + { ${script_dir}/gf_methods $i -ACD -X >> ${1}; } || { echo "gf_methods $i FAILED" >> ${1}; ((++failed)); } + done + + echo "====SSE2,SSE3,SSSE3,SSE4_2 support..." >> ${1} + export ax_cv_have_sse_ext=no + export ax_cv_have_sse2_ext=yes + export ax_cv_have_sse3_ext=yes + export ax_cv_have_ssse3_ext=yes + export ax_cv_have_sse41_ext=no + export ax_cv_have_sse42_ext=yes + export ax_cv_have_pclmuldq_ext=no + { ./configure --enable-debug-func && make clean && make; } || { echo "FAIL" >> ${1}; ((++failed)); } + for i in 128 64 32 16 8 4; do + { ${script_dir}/gf_methods $i -ACD -X >> ${1}; } || { echo "gf_methods $i FAILED" >> ${1}; ((++failed)); } + done + + echo "====FULL SIMD support..." >> ${1} + { ./configure --enable-debug-func && make clean && make; } || { echo "FAIL" >> ${1}; ((++failed)); } + for i in 128 64 32 16 8 4; do + { ${script_dir}/gf_methods $i -ACD -X >> ${1}; } || { echo "gf_methods $i FAILED" >> ${1}; ((++failed)); } + done + + return ${failed} +} + +runtime_intel_env() { + failed=0 + + # compile a build with full SIMD support + { ./configure --enable-debug-func && make clean && make; } || { echo "Compile FAILED" >> ${1}; return 1; } + + echo "====NO SIMD support..." >> ${1} + export GF_COMPLETE_DISABLE_SSE2=1 + export GF_COMPLETE_DISABLE_SSE3=1 + export GF_COMPLETE_DISABLE_SSSE3=1 + export GF_COMPLETE_DISABLE_SSE4=1 + export GF_COMPLETE_DISABLE_SSE4_PCLMUL=1 + for i in 128 64 32 16 8 4; do + { ${script_dir}/gf_methods $i -ACD -X >> ${1}; } || { echo "gf_methods $i FAILED" >> ${1}; ((++failed)); } + done + + echo "====SSE2 support..." >> ${1} + unset GF_COMPLETE_DISABLE_SSE2 + export GF_COMPLETE_DISABLE_SSE3=1 + export GF_COMPLETE_DISABLE_SSSE3=1 + export GF_COMPLETE_DISABLE_SSE4=1 + export GF_COMPLETE_DISABLE_SSE4_PCLMUL=1 + for i in 128 64 32 16 8 4; do + { ${script_dir}/gf_methods $i -ACD -X >> ${1}; } || { echo "gf_methods $i FAILED" >> ${1}; ((++failed)); } + done + + echo "====SSE2,SSE3 support..." >> ${1} + unset GF_COMPLETE_DISABLE_SSE2 + unset GF_COMPLETE_DISABLE_SSE3 + export GF_COMPLETE_DISABLE_SSSE3=1 + export GF_COMPLETE_DISABLE_SSE4=1 + export GF_COMPLETE_DISABLE_SSE4_PCLMUL=1 + for i in 128 64 32 16 8 4; do + { ${script_dir}/gf_methods $i -ACD -X >> ${1}; } || { echo "gf_methods $i FAILED" >> ${1}; ((++failed)); } + done + + echo "====SSE2,SSE3,SSSE3 support..." >> ${1} + unset GF_COMPLETE_DISABLE_SSE2 + unset GF_COMPLETE_DISABLE_SSE3 + unset GF_COMPLETE_DISABLE_SSSE3 + export GF_COMPLETE_DISABLE_SSE4=1 + export GF_COMPLETE_DISABLE_SSE4_PCLMUL=1 + for i in 128 64 32 16 8 4; do + { ${script_dir}/gf_methods $i -ACD -X >> ${1}; } || { echo "gf_methods $i FAILED" >> ${1}; ((++failed)); } + done + + echo "====SSE2,SSE3,SSSE3,SSE4_1 support..." >> ${1} + unset GF_COMPLETE_DISABLE_SSE2 + unset GF_COMPLETE_DISABLE_SSE3 + unset GF_COMPLETE_DISABLE_SSSE3 + unset GF_COMPLETE_DISABLE_SSE4 + export GF_COMPLETE_DISABLE_SSE4_PCLMUL=1 + for i in 128 64 32 16 8 4; do + { ${script_dir}/gf_methods $i -ACD -X >> ${1}; } || { echo "gf_methods $i FAILED" >> ${1}; ((++failed)); } + done + + echo "====SSE2,SSE3,SSSE3,SSE4_2 support..." >> ${1} + unset GF_COMPLETE_DISABLE_SSE2 + unset GF_COMPLETE_DISABLE_SSE3 + unset GF_COMPLETE_DISABLE_SSSE3 + unset GF_COMPLETE_DISABLE_SSE4 + export GF_COMPLETE_DISABLE_SSE4_PCLMUL=1 + for i in 128 64 32 16 8 4; do + { ${script_dir}/gf_methods $i -ACD -X >> ${1}; } || { echo "gf_methods $i FAILED" >> ${1}; ((++failed)); } + done + + echo "====FULL SIMD support..." >> ${1} + unset GF_COMPLETE_DISABLE_SSE2 + unset GF_COMPLETE_DISABLE_SSE3 + unset GF_COMPLETE_DISABLE_SSSE3 + unset GF_COMPLETE_DISABLE_SSE4 + unset GF_COMPLETE_DISABLE_SSE4_PCLMUL + for i in 128 64 32 16 8 4; do + { ${script_dir}/gf_methods $i -ACD -X >> ${1}; } || { echo "gf_methods $i FAILED" >> ${1}; ((++failed)); } + done + + return ${failed} +} + +test_runtime() { + rm -f ${results}.left + rm -f ${results}.right + + case $host_cpu in + aarch64*|arm*) + runtime_arm_flags ${results}.left + runtime_arm_env ${results}.right + ;; + i[[3456]]86*|x86_64*|amd64*) + runtime_intel_flags ${results}.left + runtime_intel_env ${results}.right + ;; + esac + + echo "======LEFT======" > ${results} + cat ${results}.left >> ${results} + echo "======RIGHT======" >> ${results} + cat ${results}.right >> ${results} + echo "======RESULT======" >> ${results} + if diff "${results}.left" "${results}.right"; then + echo SUCCESS >> ${results} + return 0 + else + echo SUCCESS >> ${results} + return 1 + fi +} + +cd ${script_dir}/.. +rm -f ${results} + +test_$1 +exit $? diff --git a/IDA_new/gf-complete/tools/test_simd_qemu.sh b/IDA_new/gf-complete/tools/test_simd_qemu.sh new file mode 100755 index 0000000..5771874 --- /dev/null +++ b/IDA_new/gf-complete/tools/test_simd_qemu.sh @@ -0,0 +1,258 @@ +#!/bin/bash -e + +# This script will use QEMU to test gf-complete especially SIMD support +# on different architectures and cpus. It will boot a qemu machine +# and run an Ubuntu cloud image. All testing will happen inside the +# QEMU machine. + +# The following packages are required: +# qemu-system-aarch64 +# qemu-system-arm +# qemu-system-x86_64 +# genisoimage + + +script_dir="$( cd "$( dirname "${BASH_SOURCE[0]}" )" && pwd )" +qemu_dir="${script_dir}/.qemu" +ssh_port=2222 +ssh_pubkey_file="${qemu_dir}/qemu.pub" +ssh_key_file="${qemu_dir}/qemu" + +mkdir -p "${qemu_dir}" + +cleanup() { + if [[ -n "$(jobs -p)" ]]; then + echo killing qemu processes "$(jobs -p)" + kill $(jobs -p) + fi +} + +trap cleanup EXIT + +start_qemu() { + arch=$1 + cpu=$2 + + image_version="xenial" + image_url_base="http://cloud-images.ubuntu.com/${image_version}/current" + + case $arch in + i[[3456]]86*|x86_64*|amd64*) + image_kernel="${image_version}-server-cloudimg-amd64-vmlinuz-generic" + image_initrd="${image_version}-server-cloudimg-amd64-initrd-generic" + image_disk="${image_version}-server-cloudimg-amd64-disk1.img" + ;; + aarch64*) + image_kernel="${image_version}-server-cloudimg-arm64-vmlinuz-generic" + image_initrd="${image_version}-server-cloudimg-arm64-initrd-generic" + image_disk="${image_version}-server-cloudimg-arm64-disk1.img" + ;; + arm*) + image_kernel="${image_version}-server-cloudimg-armhf-vmlinuz-lpae" + image_initrd="${image_version}-server-cloudimg-armhf-initrd-generic-lpae" + image_disk="${image_version}-server-cloudimg-armhf-disk1.img" + ;; + *) die "Unsupported arch" ;; + esac + + [[ -f ${qemu_dir}/${image_kernel} ]] || wget -O ${qemu_dir}/${image_kernel} ${image_url_base}/unpacked/${image_kernel} + [[ -f ${qemu_dir}/${image_initrd} ]] || wget -O ${qemu_dir}/${image_initrd} ${image_url_base}/unpacked/${image_initrd} + [[ -f ${qemu_dir}/${image_disk} ]] || wget -O ${qemu_dir}/${image_disk} ${image_url_base}/${image_disk} + + #create a delta disk to keep the original image clean + delta_disk="${qemu_dir}/disk.img" + rm -f ${delta_disk} + qemu-img create -q -f qcow2 -b "${qemu_dir}/${image_disk}" ${delta_disk} + + # generate an ssh keys + [[ -f ${ssh_pubkey_file} ]] || ssh-keygen -q -N "" -f ${ssh_key_file} + + # create a config disk to set the SSH keys + cat > "${qemu_dir}/meta-data" < "${qemu_dir}/user-data" <&2 + exit 1 +fi + +op=$1 +w=$2 + +shift ; shift + +method="$*" + +if [ $op != M -a $op != D -a $op != R -a $op != B ]; then + echo 'usage sh time_tool.sh M|D|R|B w method' >&2 + echo 'You have to specify a test: ' >&2 + echo ' M=Multiplication' >&2 + echo ' D=Division' >&2 + echo ' R=Regions' >&2 + echo ' B=Best-Region' >&2 + exit 1 +fi + +# First, use a 16K buffer to test the performance of single multiplies. + +fac=`echo $w | awk '{ n = $1; while (n != 0 && n%2==0) n /= 2; print n }'` +if [ $fac -eq 0 ]; then + echo 'usage sh time_tool.sh M|D|R|B w method' >&2 + echo 'Bad w' >&2 + exit 1 +fi + +bsize=16384 +bsize=`echo $bsize $fac | awk '{ print $1 * $2 }'` + +if [ `./gf_time $w M -1 $bsize 1 $method 2>&1 | wc | awk '{ print $1 }'` -gt 2 ]; then + echo 'usage sh time_tool.sh w method' >&2 + echo "Bad method" + exit 1 +fi + +if [ $op = M -o $op = D ]; then + iter=1 + c1=`./gf_time $w $op -1 $bsize $iter $method` + t=`echo $c1 | awk '{ printf "%d\n", $4*100 }'` + s=`echo $c1 | awk '{ print $8 }'` + bs=$s + + while [ $t -lt 1 ]; do + bs=$s + iter=`echo $iter | awk '{ print $1*2 }'` + c1=`./gf_time $w $op -1 $bsize $iter $method` + t=`echo $c1 | awk '{ printf "%d\n", $4*100 }'` + s=`echo $c1 | awk '{ print $8 }'` + done + + echo $op $bs | awk '{ printf "%s speed (MB/s): %8.2lf W-Method: ", $1, $2 }' + echo $w $method + exit 0 +fi + +bsize=16384 +bsize=`echo $bsize $fac | awk '{ print $1 * $2 }'` + +best=0 +while [ $bsize -le 4194304 ]; do + iter=1 + c1=`./gf_time $w G -1 $bsize $iter $method` + t=`echo $c1 | awk '{ printf "%d\n", $6*500 }'` + s=`echo $c1 | awk '{ print $10 }'` + bs=$s + + while [ $t -lt 1 ]; do + bs=$s + iter=`echo $iter | awk '{ print $1*2 }'` + c1=`./gf_time $w G -1 $bsize $iter $method` + t=`echo $c1 | awk '{ printf "%d\n", $6*500 }'` + s=`echo $c1 | awk '{ print $10 }'` + done + if [ $bsize -lt 1048576 ]; then + str=`echo $bsize | awk '{ printf "%3dK\n", $1/1024 }'` + else + str=`echo $bsize | awk '{ printf "%3dM\n", $1/1024/1024 }'` + fi + if [ $op = R ]; then + echo $str $bs | awk '{ printf "Region Buffer-Size: %4s (MB/s): %8.2lf W-Method: ", $1, $2 }' + echo $w $method + fi + best=`echo $best $bs | awk '{ print ($1 > $2) ? $1 : $2 }'` + bsize=`echo $bsize | awk '{ print $1 * 2 }'` +done +echo $best | awk '{ printf "Region Best (MB/s): %8.2lf W-Method: ", $1 }' +echo $w $method diff --git a/IDA_new/ida_gf65_paper1 b/IDA_new/ida_gf65_paper1 new file mode 100755 index 0000000000000000000000000000000000000000..8c8884552895bce82c28216151d3c9df4fd943d3 GIT binary patch literal 504456 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 HT#Ek#VWfnc literal 0 HcmV?d00001 diff --git a/IDA_new/ida_gf65_paper1.cpp b/IDA_new/ida_gf65_paper1.cpp new file mode 100644 index 0000000..c49e24e --- /dev/null +++ b/IDA_new/ida_gf65_paper1.cpp @@ -0,0 +1,619 @@ + + +#include +#include +#include +#include +#include +#include +#include +#include +#include // std::random_shuffle +#include // std::vector +#include +#include + + +extern "C" { + #include "jerasure.h" +} + +typedef unsigned long mylong; +#define LLUI (long long unsigned int) + + +using namespace std; + +string cloud[5]={"dropboxida1","googleida1","onedriveida2","onedriveida1","pcloudida1"}; + + +void display(mylong *mat, int r, int c) { + for(int i=0;imultiply.w64(gf,m1[i*c1+k], m2[k*c2+j]); + } + } + } + return product; +} + + + +int invert_matrix(gf_t *gf, mylong *mat, mylong *inv, int rows) +{ + int cols, i, j, k, x, rs2; + int row_start; + mylong tmp, inverse; + + cols = rows; + + k = 0; + for (i = 0; i < rows; i++) { + for (j = 0; j < cols; j++) { + inv[k] = (i == j) ? 1 : 0; + k++; + } + } +// display(inv, rows, rows); +// printf("\n"); + + /* First -- convert into upper triangular */ + for (i = 0; i < cols; i++) { + row_start = cols*i; + + /* Swap rows if we ave a zero i,i element. If we can't swap, then the + matrix was not invertible */ + + if (mat[row_start+i] == 0) { + for (j = i+1; j < rows && mat[cols*j+i] == 0; j++) ; + if (j == rows) return -1; + rs2 = j*cols; + for (k = 0; k < cols; k++) { + tmp = mat[row_start+k]; + mat[row_start+k] = mat[rs2+k]; + mat[rs2+k] = tmp; + tmp = inv[row_start+k]; + inv[row_start+k] = inv[rs2+k]; + inv[rs2+k] = tmp; + } + } + + /* Multiply the row by 1/element i,i */ + tmp = mat[row_start+i]; + if (tmp != 1) { + inverse = gf->divide.w64(gf,1, tmp); + for (j = 0; j < cols; j++) { + mat[row_start+j] = gf->multiply.w64(gf,mat[row_start+j], inverse); + inv[row_start+j] = gf->multiply.w64(gf,inv[row_start+j], inverse); + } + } + + /* Now for each j>i, add A_ji*Ai to Aj */ + k = row_start+i; + for (j = i+1; j != cols; j++) { + k += cols; + if (mat[k] != 0) { + if (mat[k] == 1) { + rs2 = cols*j; + for (x = 0; x < cols; x++) { + mat[rs2+x] ^= mat[row_start+x]; + inv[rs2+x] ^= inv[row_start+x]; + } + } else { + tmp = mat[k]; + rs2 = cols*j; + for (x = 0; x < cols; x++) { + mat[rs2+x] ^= gf->multiply.w64(gf,tmp, mat[row_start+x]); + inv[rs2+x] ^= gf->multiply.w64(gf,tmp, inv[row_start+x]); + } + } + } + } + } + + /* Now the matrix is upper triangular. Start at the top and multiply down */ + + for (i = rows-1; i >= 0; i--) { + row_start = i*cols; + for (j = 0; j < i; j++) { + rs2 = j*cols; + if (mat[rs2+i] != 0) { + tmp = mat[rs2+i]; + mat[rs2+i] = 0; + for (k = 0; k < cols; k++) { + inv[rs2+k] ^= gf->multiply.w64(gf,tmp, inv[row_start+k]); + } + } + } + } + +/* printf("mat\n"); + display(mat, rows, rows); + printf("\n"); + printf("inv\n"); + display(inv, rows, rows); + printf("\n"); +*/ + return 0; +} + + + + +int invertible_matrix(gf_t *gf, int *mat, int rows, int w) +{ + int cols, i, j, k, x, rs2; + int row_start; + mylong tmp, inverse; + + cols = rows; + + /* First -- convert into upper triangular */ + for (i = 0; i < cols; i++) { + row_start = cols*i; + + /* Swap rows if we ave a zero i,i element. If we can't swap, then the + matrix was not invertible */ + + if (mat[row_start+i] == 0) { + for (j = i+1; j < rows && mat[cols*j+i] == 0; j++) ; + if (j == rows) return 0; + rs2 = j*cols; + for (k = 0; k < cols; k++) { + tmp = mat[row_start+k]; + mat[row_start+k] = mat[rs2+k]; + mat[rs2+k] = tmp; + } + } + + /* Multiply the row by 1/element i,i */ + tmp = mat[row_start+i]; + if (tmp != 1) { + inverse = gf->divide.w64(gf,1, tmp); + for (j = 0; j < cols; j++) { + mat[row_start+j] = gf->multiply.w64(gf,mat[row_start+j], inverse); + } + } + + /* Now for each j>i, add A_ji*Ai to Aj */ + k = row_start+i; + for (j = i+1; j != cols; j++) { + k += cols; + if (mat[k] != 0) { + if (mat[k] == 1) { + rs2 = cols*j; + for (x = 0; x < cols; x++) { + mat[rs2+x] ^= mat[row_start+x]; + } + } else { + tmp = mat[k]; + rs2 = cols*j; + for (x = 0; x < cols; x++) { + mat[rs2+x] ^= gf->multiply.w64(gf,tmp,mat[row_start+x]); + } + } + } + } + } + return 1; +} + + + + + +mylong* readFullFile(char* filename,int n, int t, mylong& sizeFile, mylong & padded_size) { + + ifstream stream(filename, ios::in | ios::binary | ios::ate); +// ifstream stream("lena_small.png", ios::in | ios::binary | ios::ate); +// ifstream stream("/home/couturie/Downloads/CARCARIASS.zip", ios::in | ios::binary | ios::ate); + + sizeFile=stream.tellg(); +//cout << sizeFile << std::endl; + stream.seekg(0, ios::beg); + + + + + + + vector contents((istreambuf_iterator(stream)), istreambuf_iterator()); + + + + + + + + //make padding, we need to pad to be divisible by 8*t, we + if((sizeFile+8)%(8*t)!=0) { +// cout<<(int)(sizeFile/(8*t))<(p_contents); + + padded_size=contents.size()/8; + + mylong *p_contents2=new mylong[padded_size]; + memcpy(p_contents2,p_contents,sizeof(mylong)*padded_size); + //mylong *p_contents2=(mylong*)p_contents; + + p_contents2[0]=sizeFile; + + + + +/* for(int i=0;i=0;i--) { + res<<=8; + res+=p_contents[i]; + } + + cout << "convert val " << (long)res << endl; + + res=0; + for(int i=16-1;i>=8;i--) { + res<<=8; + res+=p_contents[i]; + } + + cout << "convert val " << (long)res << endl; + */ + + return p_contents2; +} + +void sendChunk(string name,int cloud_id) { + stringstream ss; + ss <<"rclone copy "<n) { + cout<<"pb t>n"< elapsed_seconds = end-start; + total_time+=elapsed_seconds.count(); +// cout << "elapsed time: " << elapsed_seconds.count() << "s\n"; + +// display(matC,t,t); + +// thread th[n]; + //Save trunks + for(int i=0;i myvector; + + // set some values: + for (int i=0; i(&matS2[1]); +// saveFile(reconstucted_data, "file.dat",new_size); + return 0; +} + + diff --git a/IDA_new/jerasure b/IDA_new/jerasure new file mode 160000 index 0000000..de1739c --- /dev/null +++ b/IDA_new/jerasure @@ -0,0 +1 @@ +Subproject commit de1739cc8483696506829b52e7fda4f6bb195e6a -- 2.39.5