├── .gitattributes ├── .gitignore ├── CMakeLists.txt ├── LICENSE ├── README.md ├── cm256.cpp ├── cm256.h ├── cmake └── test │ ├── test_arm_neon.cxx │ ├── test_x86_avx.cxx │ ├── test_x86_avx2.cxx │ ├── test_x86_avx512.cxx │ ├── test_x86_sse2.cxx │ ├── test_x86_sse3.cxx │ ├── test_x86_sse41.cxx │ ├── test_x86_sse42.cxx │ └── test_x86_ssse3.cxx ├── export.h ├── gf256.cpp ├── gf256.h ├── libcm256cc.pc.in ├── sse2neon.h └── unit_test ├── UDPSocket.cpp ├── UDPSocket.h ├── cm256_test.cpp ├── data.h ├── example0.cpp ├── example0.h ├── example1.cpp ├── example1.h ├── mainutils.cpp ├── mainutils.h ├── matrix_test.sln ├── matrix_test.vcxproj ├── matrix_test.vcxproj.filters ├── receive.cpp └── transmit.cpp /.gitattributes: -------------------------------------------------------------------------------- 1 | # Auto detect text files and perform LF normalization 2 | * text=auto 3 | 4 | # Custom for Visual Studio 5 | *.cs diff=csharp 6 | 7 | # Standard to msysgit 8 | *.doc diff=astextplain 9 | *.DOC diff=astextplain 10 | *.docx diff=astextplain 11 | *.DOCX diff=astextplain 12 | *.dot diff=astextplain 13 | *.DOT diff=astextplain 14 | *.pdf diff=astextplain 15 | *.PDF diff=astextplain 16 | *.rtf diff=astextplain 17 | *.RTF diff=astextplain 18 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # Windows image file caches 2 | Thumbs.db 3 | ehthumbs.db 4 | 5 | # Folder config file 6 | Desktop.ini 7 | 8 | # Recycle Bin used on file shares 9 | $RECYCLE.BIN/ 10 | 11 | # Windows Installer files 12 | *.cab 13 | *.msi 14 | *.msm 15 | *.msp 16 | 17 | # Windows shortcuts 18 | *.lnk 19 | 20 | # MSVC temp files 21 | *.obj 22 | *.log 23 | *.ilk 24 | *.pdb 25 | *.tlog 26 | *.idb 27 | *.opensdf 28 | *.sdf 29 | *.user 30 | *.suo 31 | 32 | # ========================= 33 | # Operating System Files 34 | # ========================= 35 | 36 | # OSX 37 | # ========================= 38 | 39 | .DS_Store 40 | .AppleDouble 41 | .LSOverride 42 | 43 | # Thumbnails 44 | ._* 45 | 46 | # Files that might appear on external disk 47 | .Spotlight-V100 48 | .Trashes 49 | 50 | # Directories potentially created on remote AFP share 51 | .AppleDB 52 | .AppleDesktop 53 | Network Trash Folder 54 | Temporary Items 55 | .apdisk 56 | 57 | .cproject 58 | .project 59 | build/ 60 | builds/ 61 | 62 | # CLion project directory 63 | .idea 64 | -------------------------------------------------------------------------------- /CMakeLists.txt: -------------------------------------------------------------------------------- 1 | cmake_minimum_required(VERSION 3.0) 2 | 3 | # use, i.e. don't skip the full RPATH for the build tree 4 | set(CMAKE_SKIP_BUILD_RPATH FALSE) 5 | 6 | # when building, don't use the install RPATH already 7 | # (but later on when installing) 8 | set(CMAKE_BUILD_WITH_INSTALL_RPATH FALSE) 9 | 10 | SET(CMAKE_INSTALL_RPATH "${CMAKE_INSTALL_PREFIX}/lib") 11 | 12 | # add the automatically determined parts of the RPATH 13 | # which point to directories outside the build tree to the install RPATH 14 | set(CMAKE_INSTALL_RPATH_USE_LINK_PATH TRUE) 15 | 16 | project(cm256cc) 17 | 18 | set(CMAKE_CXX_STANDARD 11) 19 | set(CMAKE_CXX_STANDARD_REQUIRED ON) 20 | set(CMAKE_CXX_EXTENSIONS OFF) 21 | 22 | set(MAJOR_VERSION 1) 23 | set(MINOR_VERSION 1) 24 | set(PATCH_VERSION 0) 25 | set(PACKAGE libcm256cc) 26 | set(VERSION_STRING ${MAJOR_VERSION}.${MINOR_VERSION}.${PATCH_VERSION}) 27 | set(VERSION ${VERSION_STRING}) 28 | 29 | option(BUILD_TOOLS "Build unit test tools" ON) 30 | 31 | include(GNUInstallDirs) 32 | set(LIB_INSTALL_DIR "${CMAKE_INSTALL_LIBDIR}") # "lib" or "lib64" 33 | 34 | if (BUILD_TYPE MATCHES RELEASE) 35 | set(CMAKE_BUILD_TYPE "Release") 36 | elseif (BUILD_TYPE MATCHES RELEASEWITHDBGINFO) 37 | set(CMAKE_BUILD_TYPE "ReleaseWithDebugInfo") 38 | elseif (BUILD_TYPE MATCHES DEBUG) 39 | set(CMAKE_BUILD_TYPE "Debug") 40 | else() 41 | set(CMAKE_BUILD_TYPE "Release") 42 | endif() 43 | 44 | ############################################################################## 45 | 46 | set(TEST_DIR ${PROJECT_SOURCE_DIR}/cmake/test) 47 | 48 | # Clang or AppleClang (see CMP0025) 49 | if(NOT DEFINED C_CLANG AND CMAKE_CXX_COMPILER_ID MATCHES "Clang") 50 | set(C_CLANG 1) 51 | endif() 52 | if(NOT DEFINED C_GCC AND CMAKE_CXX_COMPILER_ID MATCHES "GNU") 53 | set(C_GCC 1) 54 | endif() 55 | 56 | # Detect current compilation architecture and create standard definitions 57 | # ======================================================================= 58 | include(CheckSymbolExists) 59 | function(detect_architecture symbol arch) 60 | if (NOT DEFINED ARCHITECTURE) 61 | set(CMAKE_REQUIRED_QUIET 1) 62 | check_symbol_exists("${symbol}" "" ARCHITECTURE_${arch}) 63 | unset(CMAKE_REQUIRED_QUIET) 64 | 65 | # The output variable needs to be unique across invocations otherwise 66 | # CMake's crazy scope rules will keep it defined 67 | if (ARCHITECTURE_${arch}) 68 | set(ARCHITECTURE "${arch}" PARENT_SCOPE) 69 | set(ARCHITECTURE_${arch} 1 PARENT_SCOPE) 70 | add_definitions(-DARCHITECTURE_${arch}=1) 71 | endif() 72 | endif() 73 | endfunction() 74 | 75 | if (NOT ENABLE_GENERIC) 76 | if (MSVC) 77 | detect_architecture("_M_AMD64" x86_64) 78 | detect_architecture("_M_IX86" x86) 79 | detect_architecture("_M_ARM" ARM) 80 | detect_architecture("_M_ARM64" ARM64) 81 | else() 82 | detect_architecture("__x86_64__" x86_64) 83 | detect_architecture("__i386__" x86) 84 | detect_architecture("__arm__" ARM) 85 | detect_architecture("__aarch64__" ARM64) 86 | endif() 87 | endif() 88 | if (NOT DEFINED ARCHITECTURE) 89 | set(ARCHITECTURE "GENERIC") 90 | set(ARCHITECTURE_GENERIC 1) 91 | add_definitions(-DARCHITECTURE_GENERIC=1) 92 | endif() 93 | message(STATUS "Target architecture: ${ARCHITECTURE}") 94 | 95 | # flag that set the minimum cpu flag requirements 96 | # used to create re-distribuitable binary 97 | if (ENABLE_DISTRIBUTION) 98 | if (${ARCHITECTURE} MATCHES "x86_64|x86") 99 | set(HAS_SSSE3 ON CACHE BOOL "SSSE3 SIMD enabled") 100 | if(C_GCC OR C_CLANG) 101 | set( CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -mssse3" ) 102 | message(STATUS "Use SSSE3 SIMD instructions") 103 | add_definitions(-DUSE_SSSE3) 104 | elseif(MSVC) 105 | set( CMAKE_CXX_FLAGS_DEBUG "${CMAKE_CXX_FLAGS_DEBUG} /arch:SSSE3" ) 106 | set( CMAKE_CXX_FLAGS_RELEASE "${CMAKE_CXX_FLAGS_RELEASE} /Oi /GL /Ot /Ox /arch:SSSE3" ) 107 | set( CMAKE_EXE_LINKER_FLAGS_RELEASE "${CMAKE_EXE_LINKER_FLAGS_RELEASE} /LTCG" ) 108 | message(STATUS "Use MSVC SSSE3 SIMD instructions") 109 | add_definitions (/D "_CRT_SECURE_NO_WARNINGS") 110 | add_definitions(-DUSE_SSSE3) 111 | endif() 112 | elseif (${ARCHITECTURE} MATCHES "ARM|ARM64") 113 | set(HAS_NEON ON CACHE BOOL "NEON SIMD enabled") 114 | message(STATUS "Use NEON SIMD instructions") 115 | add_definitions(-DUSE_NEON) 116 | endif() 117 | else () 118 | if (${ARCHITECTURE} MATCHES "x86_64|x86") 119 | try_run(RUN_SSE2 COMPILE_SSE2 ${CMAKE_BINARY_DIR}/tmp ${TEST_DIR}/test_x86_sse2.cxx COMPILE_DEFINITIONS -msse2 -O0) 120 | if(COMPILE_SSE2 AND RUN_SSE2 EQUAL 0) 121 | set(HAS_SSE2 ON CACHE BOOL "Architecture has SSSE2 SIMD enabled") 122 | if(C_GCC OR C_CLANG) 123 | set( CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -msse2" ) 124 | message(STATUS "Use SSE2 SIMD instructions") 125 | add_definitions(-DUSE_SSE2) 126 | elseif(MSVC) 127 | set( CMAKE_CXX_FLAGS_DEBUG "${CMAKE_CXX_FLAGS_DEBUG} /arch:SSE2" ) 128 | set( CMAKE_CXX_FLAGS_RELEASE "${CMAKE_CXX_FLAGS_RELEASE} /Oi /GL /Ot /Ox /arch:SSE2" ) 129 | set( CMAKE_EXE_LINKER_FLAGS_RELEASE "${CMAKE_EXE_LINKER_FLAGS_RELEASE} /LTCG" ) 130 | add_definitions (/D "_CRT_SECURE_NO_WARNINGS") 131 | add_definitions(-DUSE_SSE2) 132 | endif() 133 | else() 134 | set(HAS_SSE2 OFF CACHE BOOL "Architecture does not have SSSE2 SIMD enabled") 135 | endif() 136 | try_run(RUN_SSSE3 COMPILE_SSSE3 ${CMAKE_BINARY_DIR}/tmp ${TEST_DIR}/test_x86_ssse3.cxx COMPILE_DEFINITIONS -mssse3 -O0) 137 | if(COMPILE_SSSE3 AND RUN_SSSE3 EQUAL 0) 138 | set(HAS_SSSE3 ON CACHE BOOL "Architecture has SSSE3 SIMD enabled") 139 | if(C_GCC OR C_CLANG) 140 | set( CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -mssse3" ) 141 | message(STATUS "Use SSSE3 SIMD instructions") 142 | add_definitions(-DUSE_SSSE3) 143 | elseif(MSVC) 144 | set( CMAKE_CXX_FLAGS_DEBUG "${CMAKE_CXX_FLAGS_DEBUG} /arch:SSSE3" ) 145 | set( CMAKE_CXX_FLAGS_RELEASE "${CMAKE_CXX_FLAGS_RELEASE} /Oi /GL /Ot /Ox /arch:SSSE3" ) 146 | set( CMAKE_EXE_LINKER_FLAGS_RELEASE "${CMAKE_EXE_LINKER_FLAGS_RELEASE} /LTCG" ) 147 | message(STATUS "Use MSVC SSSE3 SIMD instructions") 148 | add_definitions (/D "_CRT_SECURE_NO_WARNINGS") 149 | add_definitions(-DUSE_SSSE3) 150 | endif() 151 | else() 152 | set(HAS_SSSE3 OFF CACHE BOOL "Architecture does not have SSSE3 SIMD enabled") 153 | endif() 154 | try_run(RUN_SSE4_1 COMPILE_SSE4_1 ${CMAKE_BINARY_DIR}/tmp ${TEST_DIR}/test_x86_sse41.cxx COMPILE_DEFINITIONS -msse4.1 -O0) 155 | if(COMPILE_SSE4_1 AND RUN_SSE4_1 EQUAL 0) 156 | set(HAS_SSE4_1 ON CACHE BOOL "Architecture has SSE 4.1 SIMD enabled") 157 | if(C_GCC OR C_CLANG) 158 | set( CMAKE_CXX_FLAGS_RELEASE "${CMAKE_CXX_FLAGS_RELEASE} -msse4.1" ) 159 | set( CMAKE_CXX_FLAGS_DEBUG "${CMAKE_CXX_FLAGS_DEBUG} -msse4.1" ) 160 | message(STATUS "Use SSE 4.1 SIMD instructions") 161 | add_definitions(-DUSE_SSE4_1) 162 | elseif(MSVC) 163 | set( CMAKE_CXX_FLAGS_DEBUG "${CMAKE_CXX_FLAGS_DEBUG} /arch:SSE4_1" ) 164 | set( CMAKE_CXX_FLAGS_RELEASE "${CMAKE_CXX_FLAGS_RELEASE} /Oi /GL /Ot /Ox /arch:SSE4_1" ) 165 | set( CMAKE_EXE_LINKER_FLAGS_RELEASE "${CMAKE_EXE_LINKER_FLAGS_RELEASE} /LTCG" ) 166 | add_definitions (/D "_CRT_SECURE_NO_WARNINGS") 167 | add_definitions(-DUSE_SSE4_1) 168 | endif() 169 | else() 170 | set(HAS_SSE4_1 OFF CACHE BOOL "Architecture does not have SSE 4.1 SIMD enabled") 171 | endif() 172 | try_run(RUN_SSE4_2 COMPILE_SSE4_2 ${CMAKE_BINARY_DIR}/tmp ${TEST_DIR}/test_x86_sse42.cxx COMPILE_DEFINITIONS -msse4.2 -O0) 173 | if(COMPILE_SSE4_2 AND RUN_SSE4_2 EQUAL 0) 174 | set(HAS_SSE4_2 ON CACHE BOOL "Architecture has SSE 4.2 SIMD enabled") 175 | if(C_GCC OR C_CLANG) 176 | set( CMAKE_CXX_FLAGS_RELEASE "${CMAKE_CXX_FLAGS_RELEASE} -msse4.2" ) 177 | set( CMAKE_CXX_FLAGS_DEBUG "${CMAKE_CXX_FLAGS_DEBUG} -msse4.2" ) 178 | message(STATUS "Use SSE 4.2 SIMD instructions") 179 | add_definitions(-DUSE_SSE4_2) 180 | elseif(MSVC) 181 | set( CMAKE_CXX_FLAGS_DEBUG "${CMAKE_CXX_FLAGS_DEBUG} /arch:SSE4_2" ) 182 | set( CMAKE_CXX_FLAGS_RELEASE "${CMAKE_CXX_FLAGS_RELEASE} /Oi /GL /Ot /Ox /arch:SSE4_2" ) 183 | set( CMAKE_EXE_LINKER_FLAGS_RELEASE "${CMAKE_EXE_LINKER_FLAGS_RELEASE} /LTCG" ) 184 | add_definitions (/D "_CRT_SECURE_NO_WARNINGS") 185 | add_definitions(-DUSE_SSE4_2) 186 | endif() 187 | else() 188 | set(HAS_SSE4_2 OFF CACHE BOOL "Architecture does not have SSE 4.2 SIMD enabled") 189 | endif() 190 | try_run(RUN_AVX COMPILE_AVX ${CMAKE_BINARY_DIR}/tmp ${TEST_DIR}/test_x86_avx.cxx COMPILE_DEFINITIONS -mavx -O0) 191 | if(COMPILE_AVX AND RUN_AVX EQUAL 0) 192 | set(HAS_AVX ON CACHE BOOL "Architecture has AVX SIMD enabled") 193 | if(C_GCC OR C_CLANG) 194 | set( CMAKE_CXX_FLAGS_RELEASE "${CMAKE_CXX_FLAGS_RELEASE} -mavx" ) 195 | set( CMAKE_CXX_FLAGS_DEBUG "${CMAKE_CXX_FLAGS_DEBUG} -mavx" ) 196 | message(STATUS "Use AVX SIMD instructions") 197 | add_definitions(-DUSE_AVX) 198 | endif() 199 | else() 200 | set(HAS_AVX OFF CACHE BOOL "Architecture does not have AVX SIMD enabled") 201 | endif() 202 | try_run(RUN_AVX2 COMPILE_AVX2 ${CMAKE_BINARY_DIR}/tmp ${TEST_DIR}/test_x86_avx2.cxx COMPILE_DEFINITIONS -mavx2 -O0) 203 | if(COMPILE_AVX2 AND RUN_AVX2 EQUAL 0) 204 | set(HAS_AVX2 ON CACHE BOOL "Architecture has AVX2 SIMD enabled") 205 | if(C_GCC OR C_CLANG) 206 | set( CMAKE_CXX_FLAGS_RELEASE "${CMAKE_CXX_FLAGS_RELEASE} -mavx2" ) 207 | set( CMAKE_CXX_FLAGS_DEBUG "${CMAKE_CXX_FLAGS_DEBUG} -mavx2" ) 208 | message(STATUS "Use AVX2 SIMD instructions") 209 | add_definitions(-DUSE_AVX2) 210 | endif() 211 | else() 212 | set(HAS_AVX2 OFF CACHE BOOL "Architecture does not have AVX2 SIMD enabled") 213 | endif() 214 | try_run(RUN_AVX512 COMPILE_AVX512 ${CMAKE_BINARY_DIR}/tmp ${TEST_DIR}/test_x86_avx512.cxx COMPILE_DEFINITIONS -mavx512f -O0) 215 | if(COMPILE_AVX512 AND RUN_AVX512 EQUAL 0) 216 | set(HAS_AVX512 ON CACHE BOOL "Architecture has AVX512 SIMD enabled") 217 | if(C_GCC OR C_CLANG) 218 | set( CMAKE_CXX_FLAGS_RELEASE "${CMAKE_CXX_FLAGS_RELEASE} -mavx512f" ) 219 | set( CMAKE_CXX_FLAGS_DEBUG "${CMAKE_CXX_FLAGS_DEBUG} -mavx512f" ) 220 | message(STATUS "Use AVX512 SIMD instructions") 221 | add_definitions(-DUSE_AVX512) 222 | endif() 223 | else() 224 | set(HAS_AVX512 OFF CACHE BOOL "Architecture does not have AVX512 SIMD enabled") 225 | endif() 226 | elseif(ARCHITECTURE_ARM) 227 | try_run(RUN_NEON COMPILE_NEON ${CMAKE_BINARY_DIR}/tmp ${TEST_DIR}/test_arm_neon.cxx COMPILE_DEFINITIONS -mfpu=neon -O0) 228 | if(COMPILE_NEON AND RUN_NEON EQUAL 0) 229 | set(HAS_NEON ON CACHE BOOL "Architecture has NEON SIMD enabled") 230 | if(C_GCC OR C_CLANG) 231 | set( CMAKE_CXX_FLAGS_RELEASE "${CMAKE_CXX_FLAGS_RELEASE} -mfpu=neon" ) 232 | set( CMAKE_CXX_FLAGS_DEBUG "${CMAKE_CXX_FLAGS_DEBUG} -mfpu=neon" ) 233 | message(STATUS "Use NEON SIMD instructions") 234 | add_definitions(-DUSE_NEON) 235 | endif() 236 | else() 237 | set(HAS_NEON OFF CACHE BOOL "Architecture does not have NEON SIMD enabled") 238 | endif() 239 | elseif(ARCHITECTURE_ARM64) 240 | # Advanced SIMD (aka NEON) is mandatory for AArch64 241 | set(HAS_NEON ON CACHE BOOL "Architecture has NEON SIMD enabled") 242 | message(STATUS "Use NEON SIMD instructions") 243 | add_definitions(-DUSE_NEON) 244 | endif() 245 | endif() 246 | 247 | # clear binary test folder 248 | FILE(REMOVE_RECURSE ${CMAKE_BINARY_DIR}/tmp) 249 | ############################################################################## 250 | 251 | if(HAS_SSSE3) 252 | message(STATUS "Architecture supports SSSE3 - OK") 253 | elseif(HAS_NEON) 254 | message(STATUS "Architecture supports Neon - OK") 255 | else() 256 | message(STATUS "Unsupported architecture - Terminated") 257 | return() 258 | endif() 259 | 260 | # Compiler flags. 261 | if(MSVC) 262 | set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -Wall ${EXTRA_FLAGS}") 263 | else() 264 | set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -Wall -Wextra -O3 -ffast-math -ftree-vectorize ${EXTRA_FLAGS}") 265 | endif() 266 | 267 | if (CMAKE_CXX_COMPILER_ID STREQUAL "GNU" AND NOT CMAKE_CXX_COMPILER_ID MATCHES "Clang") 268 | set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -fmax-errors=10") 269 | endif() 270 | set( CMAKE_CXX_FLAGS_DEBUG "${CMAKE_CXX_FLAGS_DEBUG} -std=c++11" ) 271 | set( CMAKE_CXX_FLAGS_RELEASE "${CMAKE_CXX_FLAGS_RELEASE} -std=c++11" ) 272 | add_definitions(-DNO_RESTRICT) 273 | 274 | set(cm256_SOURCES 275 | cm256.cpp 276 | gf256.cpp 277 | ) 278 | 279 | set(cm256_HEADERS 280 | cm256.h 281 | gf256.h 282 | sse2neon.h 283 | export.h 284 | ) 285 | 286 | include_directories( 287 | . 288 | ${CMAKE_CURRENT_BINARY_DIR} 289 | ${Boost_INCLUDE_DIRS} 290 | ) 291 | 292 | add_library(cm256cc SHARED 293 | ${cm256_SOURCES} 294 | ) 295 | set_target_properties(cm256cc PROPERTIES VERSION ${VERSION} SOVERSION ${MAJOR_VERSION}) 296 | 297 | # single pass test 298 | if(BUILD_TOOLS) 299 | add_executable(cm256_test 300 | unit_test/cm256_test.cpp 301 | ) 302 | 303 | target_include_directories(cm256_test PUBLIC 304 | ${PROJECT_SOURCE_DIR} 305 | ${CMAKE_CURRENT_BINARY_DIR} 306 | ) 307 | 308 | target_link_libraries(cm256_test cm256cc) 309 | 310 | # transmit side test 311 | 312 | add_executable(cm256_tx 313 | unit_test/mainutils.cpp 314 | unit_test/UDPSocket.cpp 315 | unit_test/example0.cpp 316 | unit_test/example1.cpp 317 | unit_test/transmit.cpp 318 | ) 319 | 320 | target_include_directories(cm256_tx PUBLIC 321 | ${PROJECT_SOURCE_DIR} 322 | ${CMAKE_CURRENT_BINARY_DIR} 323 | ) 324 | 325 | target_link_libraries(cm256_tx cm256cc) 326 | 327 | # receive side test 328 | 329 | add_executable(cm256_rx 330 | unit_test/mainutils.cpp 331 | unit_test/UDPSocket.cpp 332 | unit_test/example0.cpp 333 | unit_test/example1.cpp 334 | unit_test/receive.cpp 335 | ) 336 | 337 | target_include_directories(cm256_rx PUBLIC 338 | ${PROJECT_SOURCE_DIR} 339 | ${CMAKE_CURRENT_BINARY_DIR} 340 | ) 341 | 342 | target_link_libraries(cm256_rx cm256cc) 343 | endif(BUILD_TOOLS) 344 | 345 | ######################################################################## 346 | # Create Pkg Config File 347 | ######################################################################## 348 | 349 | # use space-separation format for the pc file 350 | STRING(REPLACE ";" " " CM256CC_PC_REQUIRES "${CM256CC_PC_REQUIRES}") 351 | STRING(REPLACE ";" " " CM256CC_PC_CFLAGS "${CM256CC_PC_CFLAGS}") 352 | STRING(REPLACE ";" " " CM256CC_PC_LIBS "${CM256CC_PC_LIBS}") 353 | 354 | # unset these vars to avoid hard-coded paths to cross environment 355 | IF(CMAKE_CROSSCOMPILING) 356 | UNSET(CM256CC_PC_CFLAGS) 357 | UNSET(CM256CC_PC_LIBS) 358 | ENDIF(CMAKE_CROSSCOMPILING) 359 | 360 | CONFIGURE_FILE( 361 | ${CMAKE_CURRENT_SOURCE_DIR}/libcm256cc.pc.in 362 | ${CMAKE_CURRENT_BINARY_DIR}/libcm256cc.pc 363 | @ONLY) 364 | 365 | INSTALL( 366 | FILES ${CMAKE_CURRENT_BINARY_DIR}/libcm256cc.pc 367 | DESTINATION ${LIB_INSTALL_DIR}/pkgconfig 368 | ) 369 | 370 | 371 | # Installation 372 | if(BUILD_TOOLS) 373 | install(TARGETS cm256_test cm256_tx cm256_rx DESTINATION bin) 374 | endif(BUILD_TOOLS) 375 | install(TARGETS cm256cc DESTINATION ${LIB_INSTALL_DIR}) 376 | install(FILES ${cm256_HEADERS} DESTINATION include/${PROJECT_NAME}) 377 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # cm256cc 2 | Fast GF(256) Cauchy MDS Block Erasure Codec in C++ 3 | 4 | This is the rewrite in (as much as possible) clean C++ of [cm256](https://github.com/f4exb/cm256). In some contexts like Qt programs and plugins the original cm256 library does not work. 5 | 6 | cm256cc performance is on par or even better than cm256. This is particularly true for armv7 architecture (Raspberry Pi 2 and 3) and is the most significant with Raspberry Pi 2. 7 | 8 | cm256cc is a simple library for erasure codes. From given data it generates 9 | redundant data that can be used to recover the originals. 10 | 11 | Currently only g++ is supported, other versions of MSVC than Visual Studio 2013 may work. Optimizations for both SSE3 (x86_64) and Neon (armv7) are available. 12 | 13 | The original data should be split up into equally-sized chunks. If one of these chunks 14 | is erased, the redundant data can fill in the gap through decoding. 15 | 16 | The erasure code is parameterized by three values (`OriginalCount`, `RecoveryCount`, `BlockBytes`). These are: 17 | 18 | + The number of blocks of original data (`OriginalCount`), which must be less than 256. 19 | + The number of blocks of redundant data (`RecoveryCount`), which must be no more than `256 - OriginalCount`. 20 | 21 | For example, if a file is split into 3 equal pieces and sent over a network, `OriginalCount` is 3. 22 | And if 2 additional redundant packets are generated, `RecoveryCount` is 2. 23 | In this case up to 256 - 3 = 253 additional redundant packets can be generated. 24 | 25 | 26 | ##### Building: Quick Setup 27 | 28 | This is a classical cmake project. Make sure cmake and g++ is installed in your system. create a `build` directory and cd into it. If you install the library in a custom location say `opt/install/cm256cc` use the following command line for cmake: 29 | 30 | - `cmake -Wno-dev -DCMAKE_INSTALL_PREFIX=/opt/install/cm256cc ..` 31 | 32 | Result: 33 | 34 | - Library will be installed as `/opt/install/cm256cc/lib/libcm256cc.so` 35 | - Include files will be installed in `/opt/install/cm256cc/include/cm256cc` 36 | - Binary test programs will be installed in `/opt/install/cm256cc/bin` 37 | 38 | ##### Building: Use the library 39 | 40 | Include the cm256cc library in your project and cm256.h header in your program. Have a look at example programs `cm256_test.cpp`, `transmit.cpp`and `receive.cpp` in the `unit_test` folder for usage. Consult the `cm256.h header` for details on the encoding / decoding method. 41 | 42 | ## Compilation 43 | 44 | This is a classical cmake project. You may install the software anywhere you like with the `-DCMAKE_INSTALL_PREFIX` definition on the cmake command line. 45 | 46 | The cmake file will try to find the best compiler optimization options depending on the hardware you are compiling this project. This may not be suitable if you intend to distribute the software or include it in a distribution. In this case you can use the `-DENABLE_DISTRIBUTION=1` define on the command line to have just SSSE3 optimization for the x86 based systems and still NEON optimization for arm or arm64. 47 | 48 | ## Usage 49 | 50 | Documentation is provided in the header file [cm256.h](https://github.com/catid/cm256/raw/master/cm256.h). 51 | 52 | When your application starts up it should call `isInitialized()` to verify that the library is constructed properly: 53 | 54 | ~~~ 55 | #include "cm256.h" 56 | 57 | CM256 cm256; 58 | 59 | if (!cm256.isInitialized()) { 60 | // library not initialized 61 | exit(1); 62 | } 63 | ~~~ 64 | 65 | To generate redundancy, use the `cm256_encode` function. To solve for the original data use the `cm256_decode` function. 66 | 67 | Example usage: 68 | 69 | ~~~ 70 | bool ExampleFileUsage() 71 | { 72 | CM256 cm256; 73 | 74 | if (!cm256.isInitialized()) { 75 | // library not initialized 76 | exit(1); 77 | } 78 | 79 | CM256::cm256_encoder_params params; 80 | 81 | // Number of bytes per file block 82 | params.BlockBytes = 4321; 83 | 84 | // Number of blocks 85 | params.OriginalCount = 33; 86 | 87 | // Number of additional recovery blocks generated by encoder 88 | params.RecoveryCount = 12; 89 | 90 | // Size of the original file 91 | static const int OriginalFileBytes = params.OriginalCount * params.BlockBytes; 92 | 93 | // Allocate and fill the original file data 94 | uint8_t* originalFileData = new uint8_t[OriginalFileBytes]; 95 | memset(originalFileData, 1, OriginalFileBytes); 96 | 97 | // Pointers to data 98 | CM256::cm256_block blocks[256]; 99 | for (int i = 0; i < params.OriginalCount; ++i) 100 | { 101 | blocks[i].Block = originalFileData + i * params.BlockBytes; 102 | } 103 | 104 | // Recovery data 105 | uint8_t* recoveryBlocks = new uint8_t[params.RecoveryCount * params.BlockBytes]; 106 | 107 | // Generate recovery data 108 | if (cm256.cm256_encode(params, blocks, recoveryBlocks)) 109 | { 110 | exit(1); 111 | } 112 | 113 | // Initialize the indices 114 | for (int i = 0; i < params.OriginalCount; ++i) 115 | { 116 | blocks[i].Index = CM256::cm256_get_original_block_index(params, i); 117 | } 118 | 119 | //// Simulate loss of data, subsituting a recovery block in its place //// 120 | blocks[0].Block = recoveryBlocks; // First recovery block 121 | blocks[0].Index = cm256_get_recovery_block_index(params, 0); // First recovery block index 122 | //// Simulate loss of data, subsituting a recovery block in its place //// 123 | 124 | if (cm256.cm256_decode(params, blocks)) 125 | { 126 | exit(1); 127 | } 128 | 129 | // blocks[0].Index will now be 0. 130 | 131 | delete[] originalFileData; 132 | delete[] recoveryBlocks; 133 | 134 | return true; 135 | } 136 | ~~~ 137 | 138 | The example above is just one way to use the `cm256_decode` function. 139 | 140 | This API was designed to be flexible enough for UDP/IP-based file transfer where 141 | the blocks arrive out of order. 142 | 143 | 144 | #### Comparisons with Other Libraries 145 | 146 | The approach taken in CM256 is similar to the Intel Storage Acceleration Library (ISA-L) available here: 147 | 148 | https://01.org/intel%C2%AE-storage-acceleration-library-open-source-version/downloads 149 | 150 | ISA-L more aggressively optimizes the matrix multiplication operation, which is the most expensive step of encoding. 151 | 152 | CM256 takes better advantage of the m=1 case and the first recovery symbol, which is also possible with the Vandermonde matrices supported by ISA-L. 153 | 154 | ISA-L uses a O(N^3) Gaussian elimination solver for decoding. The CM256 decoder solves the linear system using a fast O(N^2) LDU-decomposition algorithm from "Pivoting and Backward Stability of Fast Algorithms for Solving Cauchy Linear Equations" (T. Boros, T. Kailath, V. Olshevsky), which was hand-optimized for memory accesses. 155 | 156 | 157 | #### Credits 158 | 159 | This software was written entirely by Christopher A. Taylor and converted to clean C++ code by myself Edouard M. Griffiths . 160 | -------------------------------------------------------------------------------- /cm256.cpp: -------------------------------------------------------------------------------- 1 | /* 2 | C++ version: 3 | Copyright (c) 2016 Edouard M. Griffiths. All rights reserved. 4 | 5 | Copyright (c) 2015 Christopher A. Taylor. All rights reserved. 6 | 7 | Redistribution and use in source and binary forms, with or without 8 | modification, are permitted provided that the following conditions are met: 9 | 10 | * Redistributions of source code must retain the above copyright notice, 11 | this list of conditions and the following disclaimer. 12 | * Redistributions in binary form must reproduce the above copyright notice, 13 | this list of conditions and the following disclaimer in the documentation 14 | and/or other materials provided with the distribution. 15 | * Neither the name of CM256 nor the names of its contributors may be 16 | used to endorse or promote products derived from this software without 17 | specific prior written permission. 18 | 19 | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" 20 | AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 21 | IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 22 | ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE 23 | LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR 24 | CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF 25 | SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS 26 | INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN 27 | CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) 28 | ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE 29 | POSSIBILITY OF SUCH DAMAGE. 30 | */ 31 | 32 | #include "cm256.h" 33 | 34 | CM256::CM256() 35 | { 36 | m_initialized = m_gf256Ctx.isInitialized(); 37 | } 38 | 39 | CM256::~CM256() 40 | { 41 | } 42 | 43 | /* 44 | GF(256) Cauchy Matrix Overview 45 | 46 | As described on Wikipedia, each element of a normal Cauchy matrix is defined as: 47 | 48 | a_ij = 1 / (x_i - y_j) 49 | The arrays x_i and y_j are vector parameters of the matrix. 50 | The values in x_i cannot be reused in y_j. 51 | 52 | Moving beyond the Wikipedia... 53 | 54 | (1) Number of rows (R) is the range of i, and number of columns (C) is the range of j. 55 | 56 | (2) Being able to select x_i and y_j makes Cauchy matrices more flexible in practice 57 | than Vandermonde matrices, which only have one parameter per row. 58 | 59 | (3) Cauchy matrices are always invertible, AKA always full rank, AKA when treated as 60 | as linear system y = M*x, the linear system has a single solution. 61 | 62 | (4) A Cauchy matrix concatenated below a square CxC identity matrix always has rank C, 63 | Meaning that any R rows can be eliminated from the concatenated matrix and the 64 | matrix will still be invertible. This is how Reed-Solomon erasure codes work. 65 | 66 | (5) Any row or column can be multiplied by non-zero values, and the resulting matrix 67 | is still full rank. This is true for any matrix, since it is effectively the same 68 | as pre and post multiplying by diagonal matrices, which are always invertible. 69 | 70 | (6) Matrix elements with a value of 1 are much faster to operate on than other values. 71 | For instance a matrix of [1, 1, 1, 1, 1] is invertible and much faster for various 72 | purposes than [2, 2, 2, 2, 2]. 73 | 74 | (7) For GF(256) matrices, the symbols in x_i and y_j are selected from the numbers 75 | 0...255, and so the number of rows + number of columns may not exceed 256. 76 | Note that values in x_i and y_j may not be reused as stated above. 77 | 78 | In summary, Cauchy matrices 79 | are preferred over Vandermonde matrices. (2) 80 | are great for MDS erasure codes. (3) and (4) 81 | should be optimized to include more 1 elements. (5) and (6) 82 | have a limited size in GF(256), rows+cols <= 256. (7) 83 | */ 84 | 85 | /* 86 | Selected Cauchy Matrix Form 87 | 88 | The matrix consists of elements a_ij, where i = row, j = column. 89 | a_ij = 1 / (x_i - y_j), where x_i and y_j are sets of GF(256) values 90 | that do not intersect. 91 | 92 | We select x_i and y_j to just be incrementing numbers for the 93 | purposes of this library. Further optimizations may yield matrices 94 | with more 1 elements, but the benefit seems relatively small. 95 | 96 | The x_i values range from 0...(originalCount - 1). 97 | The y_j values range from originalCount...(originalCount + recoveryCount - 1). 98 | 99 | We then improve the Cauchy matrix by dividing each column by the 100 | first row element of that column. The result is an invertible 101 | matrix that has all 1 elements in the first row. This is equivalent 102 | to a rotated Vandermonde matrix, so we could have used one of those. 103 | 104 | The advantage of doing this is that operations involving the first 105 | row will be extremely fast (just memory XOR), so the decoder can 106 | be optimized to take advantage of the shortcut when the first 107 | recovery row can be used. 108 | 109 | First row element of Cauchy matrix for each column: 110 | a_0j = 1 / (x_0 - y_j) = 1 / (x_0 - y_j) 111 | 112 | Our Cauchy matrix sets first row to ones, so: 113 | a_ij = (1 / (x_i - y_j)) / a_0j 114 | a_ij = (y_j - x_0) / (x_i - y_j) 115 | a_ij = (y_j + x_0) div (x_i + y_j) in GF(256) 116 | */ 117 | 118 | //----------------------------------------------------------------------------- 119 | // Encoding 120 | 121 | void CM256::cm256_encode_block( 122 | cm256_encoder_params params, // Encoder parameters 123 | cm256_block* originals, // Array of pointers to original blocks 124 | int recoveryBlockIndex, // Return value from cm256_get_recovery_block_index() 125 | void* recoveryBlock) // Output recovery block 126 | { 127 | // If only one block of input data, 128 | if (params.OriginalCount == 1) 129 | { 130 | // No meaningful operation here, degenerate to outputting the same data each time. 131 | 132 | memcpy(recoveryBlock, originals[0].Block, params.BlockBytes); 133 | return; 134 | } 135 | // else OriginalCount >= 2: 136 | 137 | // Unroll first row of recovery matrix: 138 | // The matrix we generate for the first row is all ones, 139 | // so it is merely a parity of the original data. 140 | if (recoveryBlockIndex == params.OriginalCount) 141 | { 142 | gf256_ctx::gf256_addset_mem(recoveryBlock, originals[0].Block, originals[1].Block, params.BlockBytes); 143 | for (int j = 2; j < params.OriginalCount; ++j) 144 | { 145 | gf256_ctx::gf256_add_mem(recoveryBlock, originals[j].Block, params.BlockBytes); 146 | } 147 | return; 148 | } 149 | 150 | // TBD: Faster algorithms seem to exist for computing this matrix-vector product. 151 | 152 | // Start the x_0 values arbitrarily from the original count. 153 | const uint8_t x_0 = static_cast(params.OriginalCount); 154 | 155 | // For other rows: 156 | { 157 | const uint8_t x_i = static_cast(recoveryBlockIndex); 158 | 159 | // Unroll first operation for speed 160 | { 161 | const uint8_t y_0 = 0; 162 | const uint8_t matrixElement = m_gf256Ctx.getMatrixElement(x_i, x_0, y_0); 163 | 164 | m_gf256Ctx.gf256_mul_mem(recoveryBlock, originals[0].Block, matrixElement, params.BlockBytes); 165 | } 166 | 167 | // For each original data column, 168 | for (int j = 1; j < params.OriginalCount; ++j) 169 | { 170 | const uint8_t y_j = static_cast(j); 171 | const uint8_t matrixElement = m_gf256Ctx.getMatrixElement(x_i, x_0, y_j); 172 | 173 | m_gf256Ctx.gf256_muladd_mem(recoveryBlock, matrixElement, originals[j].Block, params.BlockBytes); 174 | } 175 | } 176 | } 177 | 178 | int CM256::cm256_encode( 179 | cm256_encoder_params params, // Encoder params 180 | cm256_block* originals, // Array of pointers to original blocks 181 | void* recoveryBlocks) // Output recovery blocks end-to-end 182 | { 183 | // Validate input: 184 | if (params.OriginalCount <= 0 || 185 | params.RecoveryCount <= 0 || 186 | params.BlockBytes <= 0) 187 | { 188 | return -1; 189 | } 190 | if (params.OriginalCount + params.RecoveryCount > 256) 191 | { 192 | return -2; 193 | } 194 | if (!originals || !recoveryBlocks) 195 | { 196 | return -3; 197 | } 198 | 199 | uint8_t* recoveryBlock = static_cast(recoveryBlocks); 200 | 201 | for (int block = 0; block < params.RecoveryCount; ++block, recoveryBlock += params.BlockBytes) 202 | { 203 | cm256_encode_block(params, originals, (params.OriginalCount + block), recoveryBlock); 204 | } 205 | 206 | return 0; 207 | } 208 | 209 | 210 | //----------------------------------------------------------------------------- 211 | // Decoding 212 | 213 | CM256::CM256Decoder::CM256Decoder(gf256_ctx& gf256Ctx) : 214 | RecoveryCount(0), 215 | OriginalCount(0), 216 | m_gf256Ctx(gf256Ctx) 217 | { 218 | } 219 | 220 | CM256::CM256Decoder::~CM256Decoder() 221 | { 222 | } 223 | 224 | bool CM256::CM256Decoder::Initialize(cm256_encoder_params& params, cm256_block* blocks) 225 | { 226 | Params = params; 227 | 228 | cm256_block* block = blocks; 229 | OriginalCount = 0; 230 | RecoveryCount = 0; 231 | 232 | // Initialize erasures to zeros 233 | for (int ii = 0; ii < params.OriginalCount; ++ii) 234 | { 235 | ErasuresIndices[ii] = 0; 236 | } 237 | 238 | // For each input block, 239 | for (int ii = 0; ii < params.OriginalCount; ++ii, ++block) 240 | { 241 | int row = block->Index; 242 | 243 | // If it is an original block, 244 | if (row < params.OriginalCount) 245 | { 246 | Original[OriginalCount++] = block; 247 | 248 | if (ErasuresIndices[row] != 0) 249 | { 250 | // Error out if two row indices repeat 251 | return false; 252 | } 253 | 254 | ErasuresIndices[row] = 1; 255 | } 256 | else 257 | { 258 | Recovery[RecoveryCount++] = block; 259 | } 260 | } 261 | 262 | // Identify erasures 263 | for (int ii = 0, indexCount = 0; ii < 256; ++ii) 264 | { 265 | if (!ErasuresIndices[ii]) 266 | { 267 | ErasuresIndices[indexCount] = static_cast( ii ); 268 | 269 | if (++indexCount >= RecoveryCount) 270 | { 271 | break; 272 | } 273 | } 274 | } 275 | 276 | return true; 277 | } 278 | 279 | void CM256::CM256Decoder::DecodeM1() 280 | { 281 | // XOR all other blocks into the recovery block 282 | uint8_t* outBlock = static_cast(Recovery[0]->Block); 283 | const uint8_t* inBlock = nullptr; 284 | 285 | // For each block, 286 | for (int ii = 0; ii < OriginalCount; ++ii) 287 | { 288 | const uint8_t* inBlock2 = static_cast(Original[ii]->Block); 289 | 290 | if (!inBlock) 291 | { 292 | inBlock = inBlock2; 293 | } 294 | else 295 | { 296 | // outBlock ^= inBlock ^ inBlock2 297 | gf256_ctx::gf256_add2_mem(outBlock, inBlock, inBlock2, Params.BlockBytes); 298 | inBlock = nullptr; 299 | } 300 | } 301 | 302 | // Complete XORs 303 | if (inBlock) 304 | { 305 | gf256_ctx::gf256_add_mem(outBlock, inBlock, Params.BlockBytes); 306 | } 307 | 308 | // Recover the index it corresponds to 309 | Recovery[0]->Index = ErasuresIndices[0]; 310 | } 311 | 312 | // Generate the LU decomposition of the matrix 313 | void CM256::CM256Decoder::GenerateLDUDecomposition(uint8_t* matrix_L, uint8_t* diag_D, uint8_t* matrix_U) 314 | { 315 | // Schur-type-direct-Cauchy algorithm 2.5 from 316 | // "Pivoting and Backward Stability of Fast Algorithms for Solving Cauchy Linear Equations" 317 | // T. Boros, T. Kailath, V. Olshevsky 318 | // Modified for practical use. I folded the diagonal parts of U/L matrices into the 319 | // diagonal one to reduce the number of multiplications to perform against the input data, 320 | // and organized the triangle matrices in memory to allow for faster SSE3 GF multiplications. 321 | 322 | // Matrix size NxN 323 | const int N = RecoveryCount; 324 | 325 | // Generators 326 | uint8_t g[256], b[256]; 327 | for (int i = 0; i < N; ++i) 328 | { 329 | g[i] = 1; 330 | b[i] = 1; 331 | } 332 | 333 | // Temporary buffer for rotated row of U matrix 334 | // This allows for faster GF bulk multiplication 335 | uint8_t rotated_row_U[256]; 336 | uint8_t* last_U = matrix_U + ((N - 1) * N) / 2 - 1; 337 | int firstOffset_U = 0; 338 | 339 | // Start the x_0 values arbitrarily from the original count. 340 | const uint8_t x_0 = static_cast(Params.OriginalCount); 341 | 342 | // Unrolling k = 0 just makes it slower for some reason. 343 | for (int k = 0; k < N - 1; ++k) 344 | { 345 | const uint8_t x_k = Recovery[k]->Index; 346 | const uint8_t y_k = ErasuresIndices[k]; 347 | 348 | // D_kk = (x_k + y_k) 349 | // L_kk = g[k] / (x_k + y_k) 350 | // U_kk = b[k] * (x_0 + y_k) / (x_k + y_k) 351 | const uint8_t D_kk = gf256_ctx::gf256_add(x_k, y_k); 352 | const uint8_t L_kk = m_gf256Ctx.gf256_div(g[k], D_kk); 353 | const uint8_t U_kk = m_gf256Ctx.gf256_mul(m_gf256Ctx.gf256_div(b[k], D_kk), gf256_ctx::gf256_add(x_0, y_k)); 354 | 355 | // diag_D[k] = D_kk * L_kk * U_kk 356 | diag_D[k] = m_gf256Ctx.gf256_mul(D_kk, m_gf256Ctx.gf256_mul(L_kk, U_kk)); 357 | 358 | // Computing the k-th row of L and U 359 | uint8_t* row_L = matrix_L; 360 | uint8_t* row_U = rotated_row_U; 361 | for (int j = k + 1; j < N; ++j) 362 | { 363 | const uint8_t x_j = Recovery[j]->Index; 364 | const uint8_t y_j = ErasuresIndices[j]; 365 | 366 | // L_jk = g[j] / (x_j + y_k) 367 | // U_kj = b[j] / (x_k + y_j) 368 | const uint8_t L_jk = m_gf256Ctx.gf256_div(g[j], gf256_ctx::gf256_add(x_j, y_k)); 369 | const uint8_t U_kj = m_gf256Ctx.gf256_div(b[j], gf256_ctx::gf256_add(x_k, y_j)); 370 | 371 | *matrix_L++ = L_jk; 372 | *row_U++ = U_kj; 373 | 374 | // g[j] = g[j] * (x_j + x_k) / (x_j + y_k) 375 | // b[j] = b[j] * (y_j + y_k) / (y_j + x_k) 376 | g[j] = m_gf256Ctx.gf256_mul(g[j], m_gf256Ctx.gf256_div(gf256_ctx::gf256_add(x_j, x_k), gf256_ctx::gf256_add(x_j, y_k))); 377 | b[j] = m_gf256Ctx.gf256_mul(b[j], m_gf256Ctx.gf256_div(gf256_ctx::gf256_add(y_j, y_k), gf256_ctx::gf256_add(y_j, x_k))); 378 | } 379 | 380 | // Do these row/column divisions in bulk for speed. 381 | // L_jk /= L_kk 382 | // U_kj /= U_kk 383 | const int count = N - (k + 1); 384 | m_gf256Ctx.gf256_div_mem(row_L, row_L, L_kk, count); 385 | m_gf256Ctx.gf256_div_mem(rotated_row_U, rotated_row_U, U_kk, count); 386 | 387 | // Copy U matrix row into place in memory. 388 | uint8_t* output_U = last_U + firstOffset_U; 389 | row_U = rotated_row_U; 390 | for (int j = k + 1; j < N; ++j) 391 | { 392 | *output_U = *row_U++; 393 | output_U -= j; 394 | } 395 | firstOffset_U -= k + 2; 396 | } 397 | 398 | // Multiply diagonal matrix into U 399 | uint8_t* row_U = matrix_U; 400 | for (int j = N - 1; j > 0; --j) 401 | { 402 | const uint8_t y_j = ErasuresIndices[j]; 403 | const int count = j; 404 | 405 | m_gf256Ctx.gf256_mul_mem(row_U, row_U, gf256_ctx::gf256_add(x_0, y_j), count); 406 | row_U += count; 407 | } 408 | 409 | const uint8_t x_n = Recovery[N - 1]->Index; 410 | const uint8_t y_n = ErasuresIndices[N - 1]; 411 | 412 | // D_nn = 1 / (x_n + y_n) 413 | // L_nn = g[N-1] 414 | // U_nn = b[N-1] * (x_0 + y_n) 415 | const uint8_t L_nn = g[N - 1]; 416 | const uint8_t U_nn = m_gf256Ctx.gf256_mul(b[N - 1], gf256_ctx::gf256_add(x_0, y_n)); 417 | 418 | // diag_D[N-1] = L_nn * D_nn * U_nn 419 | diag_D[N - 1] = m_gf256Ctx.gf256_div(m_gf256Ctx.gf256_mul(L_nn, U_nn), gf256_ctx::gf256_add(x_n, y_n)); 420 | } 421 | 422 | void CM256::CM256Decoder::Decode() 423 | { 424 | // Matrix size is NxN, where N is the number of recovery blocks used. 425 | const int N = RecoveryCount; 426 | 427 | // Start the x_0 values arbitrarily from the original count. 428 | const uint8_t x_0 = static_cast(Params.OriginalCount); 429 | 430 | // Eliminate original data from the the recovery rows 431 | for (int originalIndex = 0; originalIndex < OriginalCount; ++originalIndex) 432 | { 433 | const uint8_t* inBlock = static_cast(Original[originalIndex]->Block); 434 | const uint8_t inRow = Original[originalIndex]->Index; 435 | 436 | for (int recoveryIndex = 0; recoveryIndex < N; ++recoveryIndex) 437 | { 438 | uint8_t* outBlock = static_cast(Recovery[recoveryIndex]->Block); 439 | const uint8_t x_i = Recovery[recoveryIndex]->Index; 440 | const uint8_t y_j = inRow; 441 | const uint8_t matrixElement = m_gf256Ctx.getMatrixElement(x_i, x_0, y_j); 442 | 443 | m_gf256Ctx.gf256_muladd_mem(outBlock, matrixElement, inBlock, Params.BlockBytes); 444 | } 445 | } 446 | 447 | // Allocate matrix 448 | static const int StackAllocSize = 2048; 449 | uint8_t stackMatrix[StackAllocSize]; 450 | uint8_t* dynamicMatrix = nullptr; 451 | uint8_t* matrix = stackMatrix; 452 | const int requiredSpace = N * N; 453 | if (requiredSpace > StackAllocSize) 454 | { 455 | dynamicMatrix = new uint8_t[requiredSpace]; 456 | matrix = dynamicMatrix; 457 | } 458 | 459 | /* 460 | Compute matrix decomposition: 461 | 462 | G = L * D * U 463 | 464 | L is lower-triangular, diagonal is all ones. 465 | D is a diagonal matrix. 466 | U is upper-triangular, diagonal is all ones. 467 | */ 468 | uint8_t* matrix_U = matrix; 469 | uint8_t* diag_D = matrix_U + (N - 1) * N / 2; 470 | uint8_t* matrix_L = diag_D + N; 471 | GenerateLDUDecomposition(matrix_L, diag_D, matrix_U); 472 | 473 | /* 474 | Eliminate lower left triangle. 475 | */ 476 | // For each column, 477 | for (int j = 0; j < N - 1; ++j) 478 | { 479 | const void* block_j = Recovery[j]->Block; 480 | 481 | // For each row, 482 | for (int i = j + 1; i < N; ++i) 483 | { 484 | void* block_i = Recovery[i]->Block; 485 | const uint8_t c_ij = *matrix_L++; // Matrix elements are stored column-first, top-down. 486 | 487 | m_gf256Ctx.gf256_muladd_mem(block_i, c_ij, block_j, Params.BlockBytes); 488 | } 489 | } 490 | 491 | /* 492 | Eliminate diagonal. 493 | */ 494 | for (int i = 0; i < N; ++i) 495 | { 496 | void* block = Recovery[i]->Block; 497 | 498 | Recovery[i]->Index = ErasuresIndices[i]; 499 | 500 | m_gf256Ctx.gf256_div_mem(block, block, diag_D[i], Params.BlockBytes); 501 | } 502 | 503 | /* 504 | Eliminate upper right triangle. 505 | */ 506 | for (int j = N - 1; j >= 1; --j) 507 | { 508 | const void* block_j = Recovery[j]->Block; 509 | 510 | for (int i = j - 1; i >= 0; --i) 511 | { 512 | void* block_i = Recovery[i]->Block; 513 | const uint8_t c_ij = *matrix_U++; // Matrix elements are stored column-first, bottom-up. 514 | 515 | m_gf256Ctx.gf256_muladd_mem(block_i, c_ij, block_j, Params.BlockBytes); 516 | } 517 | } 518 | 519 | delete[] dynamicMatrix; 520 | } 521 | 522 | int CM256::cm256_decode( 523 | cm256_encoder_params params, // Encoder params 524 | cm256_block* blocks) // Array of 'originalCount' blocks as described above 525 | { 526 | if (params.OriginalCount <= 0 || 527 | params.RecoveryCount <= 0 || 528 | params.BlockBytes <= 0) 529 | { 530 | return -1; 531 | } 532 | if (params.OriginalCount + params.RecoveryCount > 256) 533 | { 534 | return -2; 535 | } 536 | if (!blocks) 537 | { 538 | return -3; 539 | } 540 | 541 | // If there is only one block, 542 | if (params.OriginalCount == 1) 543 | { 544 | // It is the same block repeated 545 | blocks[0].Index = 0; 546 | return 0; 547 | } 548 | 549 | CM256Decoder state(m_gf256Ctx); 550 | if (!state.Initialize(params, blocks)) 551 | { 552 | return -5; 553 | } 554 | 555 | // If nothing is erased, 556 | if (state.RecoveryCount <= 0) 557 | { 558 | return 0; 559 | } 560 | 561 | // If m=1, 562 | if (params.RecoveryCount == 1) 563 | { 564 | state.DecodeM1(); 565 | return 0; 566 | } 567 | 568 | // Decode for m>1 569 | state.Decode(); 570 | return 0; 571 | } 572 | -------------------------------------------------------------------------------- /cm256.h: -------------------------------------------------------------------------------- 1 | /* 2 | C++ version: 3 | Copyright (c) 2016 Edouard M. Griffiths. All rights reserved. 4 | 5 | Copyright (c) 2015 Christopher A. Taylor. All rights reserved. 6 | 7 | Redistribution and use in source and binary forms, with or without 8 | modification, are permitted provided that the following conditions are met: 9 | 10 | * Redistributions of source code must retain the above copyright notice, 11 | this list of conditions and the following disclaimer. 12 | * Redistributions in binary form must reproduce the above copyright notice, 13 | this list of conditions and the following disclaimer in the documentation 14 | and/or other materials provided with the distribution. 15 | * Neither the name of CM256 nor the names of its contributors may be 16 | used to endorse or promote products derived from this software without 17 | specific prior written permission. 18 | 19 | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" 20 | AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 21 | IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 22 | ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE 23 | LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR 24 | CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF 25 | SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS 26 | INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN 27 | CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) 28 | ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE 29 | POSSIBILITY OF SUCH DAMAGE. 30 | */ 31 | 32 | #ifndef CM256_H 33 | #define CM256_H 34 | 35 | #include 36 | #include "gf256.h" 37 | #include "export.h" 38 | 39 | class CM256CC_API CM256 40 | { 41 | public: 42 | // Encoder parameters 43 | typedef struct cm256_encoder_params_t { 44 | // Original block count < 256 45 | int OriginalCount; 46 | 47 | // Recovery block count < 256 48 | int RecoveryCount; 49 | 50 | // Number of bytes per block (all blocks are the same size in bytes) 51 | int BlockBytes; 52 | } cm256_encoder_params; 53 | 54 | // Descriptor for data block 55 | typedef struct cm256_block_t { 56 | // Pointer to data received. 57 | void* Block; 58 | 59 | // Block index. 60 | // For original data, it will be in the range 61 | // [0..(originalCount-1)] inclusive. 62 | // For recovery data, the first one's Index must be originalCount, 63 | // and it will be in the range 64 | // [originalCount..(originalCount+recoveryCount-1)] inclusive. 65 | unsigned char Index; 66 | // Ignored during encoding, required during decoding. 67 | } cm256_block; 68 | 69 | CM256(); 70 | ~CM256(); 71 | 72 | bool isInitialized() const { return m_initialized; }; 73 | 74 | /* 75 | * Cauchy MDS GF(256) encode 76 | * 77 | * This produces a set of recovery blocks that should be transmitted after the 78 | * original data blocks. 79 | * 80 | * It takes in 'originalCount' equal-sized blocks and produces 'recoveryCount' 81 | * equally-sized recovery blocks. 82 | * 83 | * The input 'originals' array allows more natural usage of the library. 84 | * The output recovery blocks are stored end-to-end in 'recoveryBlocks'. 85 | * 'recoveryBlocks' should have recoveryCount * blockBytes bytes available. 86 | * 87 | * Precondition: originalCount + recoveryCount <= 256 88 | * 89 | * When transmitting the data, the block index of the data should be sent, 90 | * and the recovery block index is also needed. The decoder should also 91 | * be provided with the values of originalCount, recoveryCount and blockBytes. 92 | * 93 | * Example wire format: 94 | * [originalCount(1 byte)] [recoveryCount(1 byte)] 95 | * [blockIndex(1 byte)] [blockData(blockBytes bytes)] 96 | * 97 | * Be careful not to mix blocks from different encoders. 98 | * 99 | * It is possible to support variable-length data by including the original 100 | * data length at the front of each message in 2 bytes, such that when it is 101 | * recovered after a loss the data length is available in the block data and 102 | * the remaining bytes of padding can be neglected. 103 | * 104 | * Returns 0 on success, and any other code indicates failure. 105 | */ 106 | int cm256_encode( 107 | cm256_encoder_params params, // Encoder parameters 108 | cm256_block* originals, // Array of pointers to original blocks 109 | void* recoveryBlocks); // Output recovery blocks end-to-end 110 | 111 | /* 112 | * Cauchy MDS GF(256) decode 113 | * 114 | * This recovers the original data from the recovery data in the provided 115 | * blocks. There should be 'originalCount' blocks in the provided array. 116 | * Recovery will always be possible if that many blocks are received. 117 | * 118 | * Provide the same values for 'originalCount', 'recoveryCount', and 119 | * 'blockBytes' used by the encoder. 120 | * 121 | * The block Index should be set to the block index of the original data, 122 | * as described in the cm256_block struct comments above. 123 | * 124 | * Recovery blocks will be replaced with original data and the Index 125 | * will be updated to indicate the original block that was recovered. 126 | * 127 | * Returns 0 on success, and any other code indicates failure. 128 | */ 129 | int cm256_decode( 130 | cm256_encoder_params params, // Encoder parameters 131 | cm256_block* blocks); // Array of 'originalCount' blocks as described above 132 | 133 | /* 134 | * Commodity functions 135 | */ 136 | 137 | // Compute the value to put in the Index member of cm256_block 138 | static inline unsigned char cm256_get_recovery_block_index(cm256_encoder_params params, int recoveryBlockIndex) 139 | { 140 | assert(recoveryBlockIndex >= 0 && recoveryBlockIndex < params.RecoveryCount); 141 | return (unsigned char)(params.OriginalCount + recoveryBlockIndex); 142 | } 143 | static inline unsigned char cm256_get_original_block_index(cm256_encoder_params params, int originalBlockIndex) 144 | { 145 | (void) params; 146 | assert(originalBlockIndex >= 0 && originalBlockIndex < params.OriginalCount); 147 | return (unsigned char)(originalBlockIndex); 148 | } 149 | 150 | private: 151 | class CM256CC_API CM256Decoder 152 | { 153 | public: 154 | CM256Decoder(gf256_ctx& gf256Ctx); 155 | ~CM256Decoder(); 156 | 157 | // Encode parameters 158 | cm256_encoder_params Params; 159 | 160 | // Recovery blocks 161 | cm256_block* Recovery[256]; 162 | int RecoveryCount; 163 | 164 | // Original blocks 165 | cm256_block* Original[256]; 166 | int OriginalCount; 167 | 168 | // Row indices that were erased 169 | uint8_t ErasuresIndices[256]; 170 | 171 | // Initialize the decoder 172 | bool Initialize(cm256_encoder_params& params, cm256_block* blocks); 173 | 174 | // Decode m=1 case 175 | void DecodeM1(); 176 | 177 | // Decode for m>1 case 178 | void Decode(); 179 | 180 | // Generate the LU decomposition of the matrix 181 | void GenerateLDUDecomposition(uint8_t* matrix_L, uint8_t* diag_D, uint8_t* matrix_U); 182 | 183 | private: 184 | gf256_ctx& m_gf256Ctx; 185 | }; 186 | 187 | // Encode one block. 188 | // Note: This function does not validate input, use with care. 189 | void cm256_encode_block( 190 | cm256_encoder_params params, // Encoder parameters 191 | cm256_block* originals, // Array of pointers to original blocks 192 | int recoveryBlockIndex, // Return value from cm256_get_recovery_block_index() 193 | void* recoveryBlock); // Output recovery block 194 | 195 | gf256_ctx m_gf256Ctx; 196 | bool m_initialized; 197 | }; 198 | 199 | 200 | #endif // CM256_H 201 | -------------------------------------------------------------------------------- /cmake/test/test_arm_neon.cxx: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | 6 | void signalHandler(int signum) { 7 | exit(signum); // SIGILL = 4 8 | } 9 | 10 | int main(int argc, char* argv[]) 11 | { 12 | signal(SIGILL, signalHandler); 13 | uint32x4_t x={0}; 14 | x=veorq_u32(x,x); 15 | return 0; 16 | } 17 | -------------------------------------------------------------------------------- /cmake/test/test_x86_avx.cxx: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | 5 | void signalHandler(int signum) { 6 | exit(signum); // SIGILL = 4 7 | } 8 | 9 | int main(int argc, char* argv[]) 10 | { 11 | signal(SIGILL, signalHandler); 12 | __m256d x = _mm256_setzero_pd(); 13 | x=_mm256_addsub_pd(x,x); 14 | return 0; 15 | } 16 | -------------------------------------------------------------------------------- /cmake/test/test_x86_avx2.cxx: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | 5 | void signalHandler(int signum) { 6 | exit(signum); // SIGILL = 4 7 | } 8 | 9 | int main(int argc, char* argv[]) 10 | { 11 | signal(SIGILL, signalHandler); 12 | __m256i x = _mm256_setzero_si256(); 13 | x=_mm256_add_epi64 (x,x); 14 | return 0; 15 | } 16 | -------------------------------------------------------------------------------- /cmake/test/test_x86_avx512.cxx: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | 6 | void signalHandler(int signum) { 7 | exit(signum); // SIGILL = 4 8 | } 9 | 10 | int main(int argc, char* argv[]) 11 | { 12 | signal(SIGILL, signalHandler); 13 | uint64_t x[8] = {0}; 14 | __m512i y = _mm512_loadu_si512((__m512i*)x); 15 | return 0; 16 | } 17 | -------------------------------------------------------------------------------- /cmake/test/test_x86_sse2.cxx: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | 5 | void signalHandler(int signum) { 6 | exit(signum); // SIGILL = 4 7 | } 8 | 9 | int main(int argc, char* argv[]) 10 | { 11 | signal(SIGILL, signalHandler); 12 | __m128i x = _mm_setzero_si128(); 13 | x=_mm_add_epi64(x,x); 14 | return 0; 15 | } 16 | -------------------------------------------------------------------------------- /cmake/test/test_x86_sse3.cxx: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | 6 | void signalHandler(int signum) { 7 | exit(signum); // SIGILL = 4 8 | } 9 | 10 | int main(int argc, char* argv[]) 11 | { 12 | signal(SIGILL, signalHandler); 13 | __m128d x = _mm_setzero_pd(); 14 | x=_mm_addsub_pd(x,x); 15 | return 0; 16 | } 17 | -------------------------------------------------------------------------------- /cmake/test/test_x86_sse41.cxx: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | 6 | void signalHandler(int signum) { 7 | exit(signum); // SIGILL = 4 8 | } 9 | 10 | int main(int argc, char* argv[]) 11 | { 12 | signal(SIGILL, signalHandler); 13 | __m128i x = _mm_setzero_si128(); 14 | __m128i a = _mm_setzero_si128(); 15 | __m128i b = _mm_setzero_si128(); 16 | x=_mm_blend_epi16(a,b,4); 17 | return 0; 18 | } 19 | -------------------------------------------------------------------------------- /cmake/test/test_x86_sse42.cxx: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | 5 | void signalHandler(int signum) { 6 | exit(signum); // SIGILL = 4 7 | } 8 | 9 | int main(int argc, char* argv[]) 10 | { 11 | signal(SIGILL, signalHandler); 12 | unsigned int x=32; 13 | x=_mm_crc32_u8(x,4); 14 | return 0; 15 | } 16 | -------------------------------------------------------------------------------- /cmake/test/test_x86_ssse3.cxx: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | 6 | void signalHandler(int signum) { 7 | exit(signum); // SIGILL = 4 8 | } 9 | 10 | int main(int argc, char* argv[]) 11 | { 12 | signal(SIGILL, signalHandler); 13 | __m128i x = _mm_setzero_si128(); 14 | x=_mm_alignr_epi8(x,x,2); 15 | return 0; 16 | } 17 | -------------------------------------------------------------------------------- /export.h: -------------------------------------------------------------------------------- 1 | /////////////////////////////////////////////////////////////////////////////////// 2 | // Copyright (C) 2018 Edouard Griffiths, F4EXB. // 3 | // // 4 | // This program is free software; you can redistribute it and/or modify // 5 | // it under the terms of the GNU General Public License as published by // 6 | // the Free Software Foundation as version 3 of the License, or // 7 | // // 8 | // This program is distributed in the hope that it will be useful, // 9 | // but WITHOUT ANY WARRANTY; without even the implied warranty of // 10 | // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // 11 | // GNU General Public License V3 for more details. // 12 | // // 13 | // You should have received a copy of the GNU General Public License // 14 | // along with this program. If not, see . // 15 | /////////////////////////////////////////////////////////////////////////////////// 16 | 17 | #ifndef __CM256CC_EXPORT_H 18 | #define __CM256CC_EXPORT_H 19 | 20 | #if defined (__GNUC__) && (__GNUC__ >= 4) 21 | # define __CM256CC_EXPORT __attribute__((visibility("default"))) 22 | # define __CM256CC_IMPORT __attribute__((visibility("default"))) 23 | 24 | #elif defined (_MSC_VER) 25 | # define __CM256CC_EXPORT __declspec(dllexport) 26 | # define __CM256CC_IMPORT __declspec(dllimport) 27 | 28 | #else 29 | # define __CM256CC_EXPORT 30 | # define __CM256CC_IMPORT 31 | #endif 32 | 33 | /* The 'CM256CC_API' controls the import/export of 'sdrbase' symbols and classes. 34 | */ 35 | #if !defined(cm256cc_STATIC) 36 | # if defined cm256cc_EXPORTS 37 | # define CM256CC_API __CM256CC_EXPORT 38 | # else 39 | # define CM256CC_API __CM256CC_IMPORT 40 | # endif 41 | #else 42 | # define CM256CC_API 43 | #endif 44 | 45 | #endif // __CM256CC_EXPORT_H -------------------------------------------------------------------------------- /gf256.cpp: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright (c) 2015 Christopher A. Taylor. All rights reserved. 3 | 4 | Redistribution and use in source and binary forms, with or without 5 | modification, are permitted provided that the following conditions are met: 6 | 7 | * Redistributions of source code must retain the above copyright notice, 8 | this list of conditions and the following disclaimer. 9 | * Redistributions in binary form must reproduce the above copyright notice, 10 | this list of conditions and the following disclaimer in the documentation 11 | and/or other materials provided with the distribution. 12 | * Neither the name of CM256 nor the names of its contributors may be 13 | used to endorse or promote products derived from this software without 14 | specific prior written permission. 15 | 16 | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" 17 | AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 18 | IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 19 | ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE 20 | LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR 21 | CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF 22 | SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS 23 | INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN 24 | CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) 25 | ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE 26 | POSSIBILITY OF SUCH DAMAGE. 27 | */ 28 | 29 | #include 30 | #include 31 | 32 | #include "gf256.h" 33 | 34 | const uint8_t gf256_ctx::GF256_GEN_POLY[GF256_GEN_POLY_COUNT] = { 35 | 0x8e, 0x95, 0x96, 0xa6, 0xaf, 0xb1, 0xb2, 0xb4, 36 | 0xb8, 0xc3, 0xc6, 0xd4, 0xe1, 0xe7, 0xf3, 0xfa, 37 | }; 38 | 39 | gf256_ctx::gf256_ctx() : 40 | initialized(false) 41 | { 42 | gf256_init_(); 43 | } 44 | 45 | gf256_ctx::~gf256_ctx() 46 | { 47 | } 48 | 49 | // Select which polynomial to use 50 | void gf256_ctx::gf255_poly_init(int polynomialIndex) 51 | { 52 | if (polynomialIndex < 0 || polynomialIndex >= GF256_GEN_POLY_COUNT) 53 | { 54 | polynomialIndex = 0; 55 | } 56 | 57 | Polynomial = (GF256_GEN_POLY[polynomialIndex] << 1) | 1; 58 | } 59 | 60 | 61 | //----------------------------------------------------------------------------- 62 | // Exponential and Log Tables 63 | 64 | // Construct EXP and LOG tables from polynomial 65 | void gf256_ctx::gf256_explog_init() 66 | { 67 | unsigned poly = Polynomial; 68 | uint8_t* exptab = GF256_EXP_TABLE; 69 | uint16_t* logtab = GF256_LOG_TABLE; 70 | 71 | logtab[0] = 512; 72 | exptab[0] = 1; 73 | for (unsigned jj = 1; jj < 255; ++jj) 74 | { 75 | unsigned next = (unsigned)exptab[jj - 1] * 2; 76 | if (next >= 256) next ^= poly; 77 | 78 | exptab[jj] = static_cast( next ); 79 | logtab[exptab[jj]] = static_cast( jj ); 80 | } 81 | 82 | exptab[255] = exptab[0]; 83 | logtab[exptab[255]] = 255; 84 | 85 | for (unsigned jj = 256; jj < 2 * 255; ++jj) 86 | { 87 | exptab[jj] = exptab[jj % 255]; 88 | } 89 | 90 | exptab[2 * 255] = 1; 91 | 92 | for (unsigned jj = 2 * 255 + 1; jj < 4 * 255; ++jj) 93 | { 94 | exptab[jj] = 0; 95 | } 96 | } 97 | 98 | 99 | //----------------------------------------------------------------------------- 100 | // Multiply and Divide Tables 101 | 102 | // Initialize MUL and DIV tables using LOG and EXP tables 103 | void gf256_ctx::gf256_muldiv_init() 104 | { 105 | // Allocate table memory 65KB x 2 106 | uint8_t* m = GF256_MUL_TABLE; 107 | uint8_t* d = GF256_DIV_TABLE; 108 | 109 | // Unroll y = 0 subtable 110 | for (int x = 0; x < 256; ++x) 111 | { 112 | m[x] = d[x] = 0; 113 | } 114 | 115 | // For each other y value, 116 | for (int y = 1; y < 256; ++y) 117 | { 118 | // Calculate log(y) for mult and 255 - log(y) for div 119 | const uint8_t log_y = static_cast(GF256_LOG_TABLE[y]); 120 | const uint8_t log_yn = 255 - log_y; 121 | 122 | // Next subtable 123 | m += 256; 124 | d += 256; 125 | 126 | // Unroll x = 0 127 | m[0] = 0; 128 | d[0] = 0; 129 | 130 | // Calculate x * y, x / y 131 | for (int x = 1; x < 256; ++x) 132 | { 133 | uint16_t log_x = GF256_LOG_TABLE[x]; 134 | 135 | m[x] = GF256_EXP_TABLE[log_x + log_y]; 136 | d[x] = GF256_EXP_TABLE[log_x + log_yn]; 137 | } 138 | } 139 | } 140 | 141 | 142 | //----------------------------------------------------------------------------- 143 | // Inverse Table 144 | 145 | // Initialize INV table using DIV table 146 | void gf256_ctx::gf256_inv_init() 147 | { 148 | for (int x = 0; x < 256; ++x) 149 | { 150 | GF256_INV_TABLE[x] = gf256_div(1, static_cast(x)); 151 | } 152 | } 153 | 154 | 155 | //----------------------------------------------------------------------------- 156 | // Multiply and Add Memory Tables 157 | 158 | /* 159 | Fast algorithm to compute m[1..8] = a[1..8] * b in GF(256) 160 | using SSE3 SIMD instruction set: 161 | 162 | Consider z = x * y in GF(256). 163 | This operation can be performed bit-by-bit. Usefully, the partial product 164 | of each bit is combined linearly with the rest. This means that the 8-bit 165 | number x can be split into its high and low 4 bits, and partial products 166 | can be formed from each half. Then the halves can be linearly combined: 167 | 168 | z = x[0..3] * y + x[4..7] * y 169 | 170 | The multiplication of each half can be done efficiently via table lookups, 171 | and the addition in GF(256) is XOR. There must be two tables that map 16 172 | input elements for the low or high 4 bits of x to the two partial products. 173 | Each value for y has a different set of two tables: 174 | 175 | z = TABLE_LO_y(x[0..3]) xor TABLE_HI_y(x[4..7]) 176 | 177 | This means that we need 16 * 2 * 256 = 8192 bytes for precomputed tables. 178 | 179 | Computing z[] = x[] * y can be performed 16 bytes at a time by using the 180 | 128-bit register operations supported by modern processors. 181 | 182 | This is efficiently realized in SSE3 using the _mm_shuffle_epi8() function 183 | provided by Visual Studio 2010 or newer in . This function 184 | uses the low bits to do a table lookup on each byte. Unfortunately the 185 | high bit of each mask byte has the special feature that it clears the 186 | output byte when it is set, so we need to make sure it's cleared by masking 187 | off the high bit of each byte before using it: 188 | 189 | clr_mask = _mm_set1_epi8(0x0f) = 0x0f0f0f0f0f0f0f0f0f0f0f0f0f0f0f0f 190 | 191 | For the low half of the partial product, clear the high bit of each byte 192 | and perform the table lookup: 193 | 194 | p_lo = _mm_and_si128(x, clr_mask) 195 | p_lo = _mm_shuffle_epi8(p_lo, TABLE_LO_y) 196 | 197 | For the high half of the partial product, shift the high 4 bits of each 198 | byte into the low 4 bits and clear the high bit of each byte, and then 199 | perform the table lookup: 200 | 201 | p_hi = _mm_srli_epi64(x, 4) 202 | p_hi = _mm_and_si128(p_hi, clr_mask) 203 | p_hi = _mm_shuffle_epi8(p_hi, TABLE_HI_y) 204 | 205 | Finally add the two partial products to form the product, recalling that 206 | addition is XOR in a Galois field: 207 | 208 | result = _mm_xor_si128(p_lo, p_hi) 209 | 210 | This crunches 16 bytes of x at a time, and the result can be stored in z. 211 | */ 212 | 213 | /* 214 | Intrinsic reference: 215 | 216 | SSE3, VS2010+, tmmintrin.h: 217 | 218 | GF256_M128 _mm_shuffle_epi8(GF256_M128 a, GF256_M128 mask); 219 | Emits the Supplemental Streaming SIMD Extensions 3 (SSSE3) instruction pshufb. This instruction shuffles 16-byte parameters from a 128-bit parameter. 220 | 221 | Pseudo-code for PSHUFB (with 128 bit operands): 222 | 223 | for i = 0 to 15 { 224 | if (SRC[(i * 8)+7] = 1 ) then 225 | DEST[(i*8)+7..(i*8)+0] <- 0; 226 | else 227 | index[3..0] <- SRC[(i*8)+3 .. (i*8)+0]; 228 | DEST[(i*8)+7..(i*8)+0] <- DEST[(index*8+7)..(index*8+0)]; 229 | endif 230 | } 231 | 232 | SSE2, VS2008+, emmintrin.h: 233 | 234 | GF256_M128 _mm_slli_epi64 (GF256_M128 a, int count); 235 | Shifts the 2 signed or unsigned 64-bit integers in a left by count bits while shifting in zeros. 236 | GF256_M128 _mm_srli_epi64 (GF256_M128 a, int count); 237 | Shifts the 2 signed or unsigned 64-bit integers in a right by count bits while shifting in zeros. 238 | GF256_M128 _mm_set1_epi8 (char b); 239 | Sets the 16 signed 8-bit integer values to b. 240 | GF256_M128 _mm_and_si128 (GF256_M128 a, GF256_M128 b); 241 | Computes the bitwise AND of the 128-bit value in a and the 128-bit value in b. 242 | GF256_M128 _mm_xor_si128 ( GF256_M128 a, GF256_M128 b); 243 | Computes the bitwise XOR of the 128-bit value in a and the 128-bit value in b. 244 | */ 245 | 246 | // Initialize the MM256 tables using gf256_mul() 247 | void gf256_ctx::gf256_muladd_mem_init() 248 | { 249 | for (int y = 0; y < 256; ++y) 250 | { 251 | uint8_t lo[16], hi[16]; 252 | 253 | // TABLE_LO_Y maps 0..15 to 8-bit partial product based on y. 254 | for (unsigned char x = 0; x < 16; ++x) 255 | { 256 | lo[x] = gf256_mul(x, static_cast( y )); 257 | hi[x] = gf256_mul(x << 4, static_cast( y )); 258 | } 259 | 260 | const GF256_M128 table_lo = _mm_set_epi8( 261 | lo[15], lo[14], lo[13], lo[12], lo[11], lo[10], lo[9], lo[8], 262 | lo[7], lo[6], lo[5], lo[4], lo[3], lo[2], lo[1], lo[0]); 263 | const GF256_M128 table_hi = _mm_set_epi8( 264 | hi[15], hi[14], hi[13], hi[12], hi[11], hi[10], hi[9], hi[8], 265 | hi[7], hi[6], hi[5], hi[4], hi[3], hi[2], hi[1], hi[0]); 266 | _mm_store_si128(MM256_TABLE_LO_Y + y, table_lo); 267 | _mm_store_si128(MM256_TABLE_HI_Y + y, table_hi); 268 | } 269 | } 270 | 271 | //----------------------------------------------------------------------------- 272 | // Initialization 273 | // 274 | // Initialize a context, filling in the tables. 275 | // 276 | // Thread-safety / Usage Notes: 277 | // 278 | // It is perfectly safe and encouraged to use a gf256_ctx object from multiple 279 | // threads. The gf256_init() is relatively expensive and should only be done 280 | // once, though it will take less than a millisecond. 281 | // 282 | // The gf256_ctx object must be aligned to 16 byte boundary. 283 | // Simply tag the object with GF256_ALIGNED to achieve this. 284 | // 285 | // Example: 286 | // static GF256_ALIGNED gf256_ctx TheGF256Context; 287 | // gf256_init(&TheGF256Context, 0); 288 | // 289 | // Returns 0 on success and other values on failure. 290 | 291 | int gf256_ctx::gf256_init_() 292 | { 293 | // Avoid multiple initialization 294 | if (initialized) 295 | { 296 | return 0; 297 | } 298 | 299 | if (!IsLittleEndian()) 300 | { 301 | fprintf(stderr, "gf256_ctx::gf256_init_: Little Endian architecture expected (code won't work without mods)\n"); 302 | return -2; 303 | } 304 | 305 | gf255_poly_init(DefaultPolynomialIndex); 306 | gf256_explog_init(); 307 | gf256_muldiv_init(); 308 | gf256_inv_init(); 309 | gf256_muladd_mem_init(); 310 | 311 | initialized = true; 312 | fprintf(stderr, "gf256_ctx::gf256_init_: initialized\n"); 313 | return 0; 314 | } 315 | 316 | //----------------------------------------------------------------------------- 317 | // Operations with context 318 | 319 | void gf256_ctx::gf256_mul_mem(void * GF256_RESTRICT vz, const void * GF256_RESTRICT vx, uint8_t y, int bytes) 320 | { 321 | // Use a single if-statement to handle special cases 322 | if (y <= 1) 323 | { 324 | if (y == 0) 325 | { 326 | memset(vz, 0, bytes); 327 | } 328 | return; 329 | } 330 | 331 | // Partial product tables; see above 332 | const GF256_M128 table_lo_y = _mm_load_si128(MM256_TABLE_LO_Y + y); 333 | const GF256_M128 table_hi_y = _mm_load_si128(MM256_TABLE_HI_Y + y); 334 | 335 | // clr_mask = 0x0f0f0f0f0f0f0f0f0f0f0f0f0f0f0f0f 336 | const GF256_M128 clr_mask = _mm_set1_epi8(0x0f); 337 | 338 | GF256_M128 * GF256_RESTRICT z16 = reinterpret_cast(vz); 339 | const GF256_M128 * GF256_RESTRICT x16 = reinterpret_cast(vx); 340 | 341 | // Handle multiples of 16 bytes 342 | while (bytes >= 16) 343 | { 344 | // See above comments for details 345 | GF256_M128 x0 = _mm_loadu_si128(x16); 346 | GF256_M128 l0 = _mm_and_si128(x0, clr_mask); 347 | x0 = _mm_srli_epi64(x0, 4); 348 | GF256_M128 h0 = _mm_and_si128(x0, clr_mask); 349 | l0 = _mm_shuffle_epi8(table_lo_y, l0); 350 | h0 = _mm_shuffle_epi8(table_hi_y, h0); 351 | _mm_storeu_si128(z16, _mm_xor_si128(l0, h0)); 352 | 353 | x16++; 354 | z16++; 355 | bytes -= 16; 356 | } 357 | 358 | uint8_t * GF256_RESTRICT z8 = reinterpret_cast(z16); 359 | const uint8_t * GF256_RESTRICT x8 = reinterpret_cast(x16); 360 | const uint8_t * GF256_RESTRICT table = GF256_MUL_TABLE + ((unsigned)y << 8); 361 | 362 | // Handle a block of 8 bytes 363 | if (bytes >= 8) 364 | { 365 | uint64_t word = table[x8[0]]; 366 | word |= (uint64_t)table[x8[1]] << 8; 367 | word |= (uint64_t)table[x8[2]] << 16; 368 | word |= (uint64_t)table[x8[3]] << 24; 369 | word |= (uint64_t)table[x8[4]] << 32; 370 | word |= (uint64_t)table[x8[5]] << 40; 371 | word |= (uint64_t)table[x8[6]] << 48; 372 | word |= (uint64_t)table[x8[7]] << 56; 373 | *(uint64_t*)z8 = word; 374 | 375 | x8 += 8; 376 | z8 += 8; 377 | bytes -= 8; 378 | } 379 | 380 | // Handle a block of 4 bytes 381 | if (bytes >= 4) 382 | { 383 | uint32_t word = table[x8[0]]; 384 | word |= (uint32_t)table[x8[1]] << 8; 385 | word |= (uint32_t)table[x8[2]] << 16; 386 | word |= (uint32_t)table[x8[3]] << 24; 387 | *(uint32_t*)z8 = word; 388 | 389 | x8 += 4; 390 | z8 += 4; 391 | bytes -= 4; 392 | } 393 | 394 | // Handle single bytes 395 | for (int i = bytes; i > 0; i--) { 396 | z8[i-1] = table[x8[i-1]]; 397 | } 398 | } 399 | 400 | void gf256_ctx::gf256_muladd_mem(void * GF256_RESTRICT vz, uint8_t y, const void * GF256_RESTRICT vx, int bytes) 401 | { 402 | // Use a single if-statement to handle special cases 403 | if (y <= 1) 404 | { 405 | if (y == 1) 406 | { 407 | gf256_add_mem(vz, vx, bytes); 408 | } 409 | return; 410 | } 411 | 412 | // Partial product tables; see above 413 | const GF256_M128 table_lo_y = _mm_load_si128(MM256_TABLE_LO_Y + y); 414 | const GF256_M128 table_hi_y = _mm_load_si128(MM256_TABLE_HI_Y + y); 415 | 416 | // clr_mask = 0x0f0f0f0f0f0f0f0f0f0f0f0f0f0f0f0f 417 | const GF256_M128 clr_mask = _mm_set1_epi8(0x0f); 418 | 419 | GF256_M128 * GF256_RESTRICT z16 = reinterpret_cast(vz); 420 | const GF256_M128 * GF256_RESTRICT x16 = reinterpret_cast(vx); 421 | 422 | // Handle multiples of 16 bytes 423 | while (bytes >= 16) 424 | { 425 | // See above comments for details 426 | GF256_M128 x0 = _mm_loadu_si128(x16); 427 | GF256_M128 l0 = _mm_and_si128(x0, clr_mask); 428 | x0 = _mm_srli_epi64(x0, 4); 429 | GF256_M128 h0 = _mm_and_si128(x0, clr_mask); 430 | l0 = _mm_shuffle_epi8(table_lo_y, l0); 431 | h0 = _mm_shuffle_epi8(table_hi_y, h0); 432 | const GF256_M128 p0 = _mm_xor_si128(l0, h0); 433 | const GF256_M128 z0 = _mm_loadu_si128(z16); 434 | _mm_storeu_si128(z16, _mm_xor_si128(p0, z0)); 435 | 436 | x16++; 437 | z16++; 438 | bytes -= 16; 439 | } 440 | 441 | uint8_t * GF256_RESTRICT z8 = reinterpret_cast(z16); 442 | const uint8_t * GF256_RESTRICT x8 = reinterpret_cast(x16); 443 | const uint8_t * GF256_RESTRICT table = GF256_MUL_TABLE + ((unsigned)y << 8); 444 | 445 | // Handle a block of 8 bytes 446 | if (bytes >= 8) 447 | { 448 | uint64_t word = table[x8[0]]; 449 | word |= (uint64_t)table[x8[1]] << 8; 450 | word |= (uint64_t)table[x8[2]] << 16; 451 | word |= (uint64_t)table[x8[3]] << 24; 452 | word |= (uint64_t)table[x8[4]] << 32; 453 | word |= (uint64_t)table[x8[5]] << 40; 454 | word |= (uint64_t)table[x8[6]] << 48; 455 | word |= (uint64_t)table[x8[7]] << 56; 456 | *(uint64_t*)z8 ^= word; 457 | 458 | x8 += 8; 459 | z8 += 8; 460 | bytes -= 8; 461 | } 462 | 463 | // Handle a block of 4 bytes 464 | if (bytes >= 4) 465 | { 466 | uint32_t word = table[x8[0]]; 467 | word |= (uint32_t)table[x8[1]] << 8; 468 | word |= (uint32_t)table[x8[2]] << 16; 469 | word |= (uint32_t)table[x8[3]] << 24; 470 | *(uint32_t*)z8 ^= word; 471 | 472 | x8 += 4; 473 | z8 += 4; 474 | bytes -= 4; 475 | } 476 | 477 | // Handle single bytes 478 | for (int i = bytes; i > 0; i--) { 479 | z8[i-1] ^= table[x8[i-1]]; 480 | } 481 | } 482 | 483 | //----------------------------------------------------------------------------- 484 | // Static operations 485 | 486 | void gf256_ctx::gf256_add_mem(void * GF256_RESTRICT vx, const void * GF256_RESTRICT vy, int bytes) 487 | { 488 | GF256_M128 * GF256_RESTRICT x16 = reinterpret_cast(vx); 489 | const GF256_M128 * GF256_RESTRICT y16 = reinterpret_cast(vy); 490 | 491 | // Handle multiples of 64 bytes 492 | while (bytes >= 64) 493 | { 494 | GF256_M128 x0 = _mm_loadu_si128(x16); 495 | GF256_M128 x1 = _mm_loadu_si128(x16 + 1); 496 | GF256_M128 x2 = _mm_loadu_si128(x16 + 2); 497 | GF256_M128 x3 = _mm_loadu_si128(x16 + 3); 498 | GF256_M128 y0 = _mm_loadu_si128(y16); 499 | GF256_M128 y1 = _mm_loadu_si128(y16 + 1); 500 | GF256_M128 y2 = _mm_loadu_si128(y16 + 2); 501 | GF256_M128 y3 = _mm_loadu_si128(y16 + 3); 502 | 503 | _mm_storeu_si128(x16, 504 | _mm_xor_si128(x0, y0)); 505 | _mm_storeu_si128(x16 + 1, 506 | _mm_xor_si128(x1, y1)); 507 | _mm_storeu_si128(x16 + 2, 508 | _mm_xor_si128(x2, y2)); 509 | _mm_storeu_si128(x16 + 3, 510 | _mm_xor_si128(x3, y3)); 511 | 512 | x16 += 4; 513 | y16 += 4; 514 | bytes -= 64; 515 | } 516 | 517 | // Handle multiples of 16 bytes 518 | while (bytes >= 16) 519 | { 520 | // x[i] = x[i] xor y[i] 521 | _mm_storeu_si128(x16, 522 | _mm_xor_si128( 523 | _mm_loadu_si128(x16), 524 | _mm_loadu_si128(y16))); 525 | 526 | x16++; 527 | y16++; 528 | bytes -= 16; 529 | } 530 | 531 | uint8_t * GF256_RESTRICT x1 = reinterpret_cast(x16); 532 | const uint8_t * GF256_RESTRICT y1 = reinterpret_cast(y16); 533 | 534 | // Handle a block of 8 bytes 535 | if (bytes >= 8) 536 | { 537 | uint64_t * GF256_RESTRICT x8 = reinterpret_cast(x1); 538 | const uint64_t * GF256_RESTRICT y8 = reinterpret_cast(y1); 539 | *x8 ^= *y8; 540 | 541 | x1 += 8; 542 | y1 += 8; 543 | bytes -= 8; 544 | } 545 | 546 | // Handle a block of 4 bytes 547 | if (bytes >= 4) 548 | { 549 | uint32_t * GF256_RESTRICT x4 = reinterpret_cast(x1); 550 | const uint32_t * GF256_RESTRICT y4 = reinterpret_cast(y1); 551 | *x4 ^= *y4; 552 | 553 | x1 += 4; 554 | y1 += 4; 555 | bytes -= 4; 556 | } 557 | 558 | // Handle final bytes 559 | for (int i = bytes; i > 0; i--) { 560 | x1[i-1] ^= y1[i-1]; 561 | } 562 | } 563 | 564 | void gf256_ctx::gf256_add2_mem(void * GF256_RESTRICT vz, const void * GF256_RESTRICT vx, const void * GF256_RESTRICT vy, int bytes) 565 | { 566 | GF256_M128 * GF256_RESTRICT z16 = reinterpret_cast(vz); 567 | const GF256_M128 * GF256_RESTRICT x16 = reinterpret_cast(vx); 568 | const GF256_M128 * GF256_RESTRICT y16 = reinterpret_cast(vy); 569 | 570 | // Handle multiples of 16 bytes 571 | while (bytes >= 16) 572 | { 573 | // z[i] = x[i] xor y[i] 574 | _mm_storeu_si128(z16, 575 | _mm_xor_si128( 576 | _mm_loadu_si128(z16), 577 | _mm_xor_si128( 578 | _mm_loadu_si128(x16), 579 | _mm_loadu_si128(y16)))); 580 | 581 | x16++; 582 | y16++; 583 | z16++; 584 | bytes -= 16; 585 | } 586 | 587 | uint8_t * GF256_RESTRICT z1 = reinterpret_cast(z16); 588 | const uint8_t * GF256_RESTRICT x1 = reinterpret_cast(x16); 589 | const uint8_t * GF256_RESTRICT y1 = reinterpret_cast(y16); 590 | 591 | // Handle a block of 8 bytes 592 | if (bytes >= 8) 593 | { 594 | uint64_t * GF256_RESTRICT z8 = reinterpret_cast(z1); 595 | const uint64_t * GF256_RESTRICT x8 = reinterpret_cast(x1); 596 | const uint64_t * GF256_RESTRICT y8 = reinterpret_cast(y1); 597 | *z8 ^= *x8 ^ *y8; 598 | 599 | x1 += 8; 600 | y1 += 8; 601 | z1 += 8; 602 | bytes -= 8; 603 | } 604 | 605 | // Handle a block of 4 bytes 606 | if (bytes >= 4) 607 | { 608 | uint32_t * GF256_RESTRICT z4 = reinterpret_cast(z1); 609 | const uint32_t * GF256_RESTRICT x4 = reinterpret_cast(x1); 610 | const uint32_t * GF256_RESTRICT y4 = reinterpret_cast(y1); 611 | *z4 ^= *x4 ^ *y4; 612 | 613 | x1 += 4; 614 | y1 += 4; 615 | z1 += 4; 616 | bytes -= 4; 617 | } 618 | 619 | // Handle final bytes 620 | for (int i = bytes; i > 0; i--) { 621 | z1[i-1] ^= x1[i-1] ^ y1[i-1]; 622 | } 623 | } 624 | 625 | void gf256_ctx::gf256_addset_mem(void * GF256_RESTRICT vz, const void * GF256_RESTRICT vx, const void * GF256_RESTRICT vy, int bytes) 626 | { 627 | GF256_M128 * GF256_RESTRICT z16 = reinterpret_cast(vz); 628 | const GF256_M128 * GF256_RESTRICT x16 = reinterpret_cast(vx); 629 | const GF256_M128 * GF256_RESTRICT y16 = reinterpret_cast(vy); 630 | 631 | // Handle multiples of 64 bytes 632 | while (bytes >= 64) 633 | { 634 | GF256_M128 x0 = _mm_loadu_si128(x16); 635 | GF256_M128 x1 = _mm_loadu_si128(x16 + 1); 636 | GF256_M128 x2 = _mm_loadu_si128(x16 + 2); 637 | GF256_M128 x3 = _mm_loadu_si128(x16 + 3); 638 | GF256_M128 y0 = _mm_loadu_si128(y16); 639 | GF256_M128 y1 = _mm_loadu_si128(y16 + 1); 640 | GF256_M128 y2 = _mm_loadu_si128(y16 + 2); 641 | GF256_M128 y3 = _mm_loadu_si128(y16 + 3); 642 | 643 | _mm_storeu_si128(z16, _mm_xor_si128(x0, y0)); 644 | _mm_storeu_si128(z16 + 1, _mm_xor_si128(x1, y1)); 645 | _mm_storeu_si128(z16 + 2, _mm_xor_si128(x2, y2)); 646 | _mm_storeu_si128(z16 + 3, _mm_xor_si128(x3, y3)); 647 | 648 | x16 += 4; 649 | y16 += 4; 650 | z16 += 4; 651 | bytes -= 64; 652 | } 653 | 654 | // Handle multiples of 16 bytes 655 | while (bytes >= 16) 656 | { 657 | // z[i] = x[i] xor y[i] 658 | _mm_storeu_si128(z16, 659 | _mm_xor_si128( 660 | _mm_loadu_si128(x16), 661 | _mm_loadu_si128(y16))); 662 | 663 | x16++; 664 | y16++; 665 | z16++; 666 | bytes -= 16; 667 | } 668 | 669 | uint8_t * GF256_RESTRICT z1 = reinterpret_cast(z16); 670 | const uint8_t * GF256_RESTRICT x1 = reinterpret_cast(x16); 671 | const uint8_t * GF256_RESTRICT y1 = reinterpret_cast(y16); 672 | 673 | // Handle a block of 8 bytes 674 | if (bytes >= 8) 675 | { 676 | uint64_t * GF256_RESTRICT z8 = reinterpret_cast(z1); 677 | const uint64_t * GF256_RESTRICT x8 = reinterpret_cast(x1); 678 | const uint64_t * GF256_RESTRICT y8 = reinterpret_cast(y1); 679 | *z8 = *x8 ^ *y8; 680 | 681 | x1 += 8; 682 | y1 += 8; 683 | z1 += 8; 684 | bytes -= 8; 685 | } 686 | 687 | // Handle a block of 4 bytes 688 | if (bytes >= 4) 689 | { 690 | uint32_t * GF256_RESTRICT z4 = reinterpret_cast(z1); 691 | const uint32_t * GF256_RESTRICT x4 = reinterpret_cast(x1); 692 | const uint32_t * GF256_RESTRICT y4 = reinterpret_cast(y1); 693 | *z4 = *x4 ^ *y4; 694 | 695 | x1 += 4; 696 | y1 += 4; 697 | z1 += 4; 698 | bytes -= 4; 699 | } 700 | 701 | // Handle final bytes 702 | for (int i = bytes; i > 0; i--) { 703 | z1[i-1] = x1[i-1] ^ y1[i-1]; 704 | } 705 | } 706 | 707 | void gf256_memswap(void * GF256_RESTRICT vx, void * GF256_RESTRICT vy, int bytes) 708 | { 709 | GF256_M128 * GF256_RESTRICT x16 = reinterpret_cast(vx); 710 | GF256_M128 * GF256_RESTRICT y16 = reinterpret_cast(vy); 711 | 712 | // Handle blocks of 16 bytes 713 | while (bytes >= 16) 714 | { 715 | GF256_M128 x0 = _mm_loadu_si128(x16); 716 | GF256_M128 y0 = _mm_loadu_si128(y16); 717 | _mm_storeu_si128(x16, y0); 718 | _mm_storeu_si128(y16, x0); 719 | 720 | bytes -= 16; 721 | ++x16; 722 | ++y16; 723 | } 724 | 725 | uint8_t * GF256_RESTRICT x1 = reinterpret_cast(x16); 726 | uint8_t * GF256_RESTRICT y1 = reinterpret_cast(y16); 727 | 728 | // Handle a block of 8 bytes 729 | if (bytes >= 8) 730 | { 731 | uint64_t * GF256_RESTRICT x8 = reinterpret_cast(x1); 732 | uint64_t * GF256_RESTRICT y8 = reinterpret_cast(y1); 733 | 734 | uint64_t temp = *x8; 735 | *x8 = *y8; 736 | *y8 = temp; 737 | 738 | x1 += 8; 739 | y1 += 8; 740 | bytes -= 8; 741 | } 742 | 743 | // Handle a block of 4 bytes 744 | if (bytes >= 4) 745 | { 746 | uint32_t * GF256_RESTRICT x4 = reinterpret_cast(x1); 747 | uint32_t * GF256_RESTRICT y4 = reinterpret_cast(y1); 748 | 749 | uint32_t temp = *x4; 750 | *x4 = *y4; 751 | *y4 = temp; 752 | 753 | x1 += 4; 754 | y1 += 4; 755 | bytes -= 4; 756 | } 757 | 758 | // Handle final bytes 759 | uint8_t temp; 760 | 761 | for (int i = bytes; i > 0; i--) { 762 | temp = x1[i-1]; x1[i-1] = y1[i-1]; y1[i-1] = temp; 763 | } 764 | } 765 | -------------------------------------------------------------------------------- /gf256.h: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright (c) 2015 Christopher A. Taylor. All rights reserved. 3 | 4 | Redistribution and use in source and binary forms, with or without 5 | modification, are permitted provided that the following conditions are met: 6 | 7 | * Redistributions of source code must retain the above copyright notice, 8 | this list of conditions and the following disclaimer. 9 | * Redistributions in binary form must reproduce the above copyright notice, 10 | this list of conditions and the following disclaimer in the documentation 11 | and/or other materials provided with the distribution. 12 | * Neither the name of CM256 nor the names of its contributors may be 13 | used to endorse or promote products derived from this software without 14 | specific prior written permission. 15 | 16 | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" 17 | AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 18 | IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 19 | ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE 20 | LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR 21 | CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF 22 | SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS 23 | INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN 24 | CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) 25 | ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE 26 | POSSIBILITY OF SUCH DAMAGE. 27 | */ 28 | 29 | #ifndef GF256_H 30 | #define GF256_H 31 | 32 | #include // uint32_t etc 33 | #include // memcpy, memset 34 | #include "export.h" 35 | 36 | // TBD: Fix the polynomial at one value and use precomputed tables here to 37 | // simplify the API for GF256.h version 2. Avoids user data alignment issues. 38 | 39 | 40 | //----------------------------------------------------------------------------- 41 | // Platform-Specific Definitions 42 | // 43 | // Edit these to port to your architecture 44 | 45 | #if defined(USE_SSSE3) 46 | 47 | #ifdef _MSC_VER 48 | 49 | // Compiler-specific 128-bit SIMD register keyword 50 | #define GF256_M128 __m128i 51 | 52 | // Compiler-specific C++11 restrict keyword 53 | #define GF256_RESTRICT_KW __restrict 54 | 55 | // Compiler-specific force inline keyword 56 | #define GF256_FORCE_INLINE __forceinline 57 | 58 | // Compiler-specific alignment keyword 59 | #define GF256_ALIGNED __declspec(align(16)) 60 | 61 | // Compiler-specific SSE headers 62 | #include // SSE3: _mm_shuffle_epi8 63 | #include // SSE2 64 | 65 | #else 66 | 67 | // Compiler-specific 128-bit SIMD register keyword 68 | #define GF256_M128 __m128i 69 | 70 | // Compiler-specific C++11 restrict keyword 71 | #define GF256_RESTRICT_KW __restrict__ 72 | 73 | // Compiler-specific force inline keyword 74 | #define GF256_FORCE_INLINE __attribute__((always_inline)) inline 75 | 76 | // Compiler-specific alignment keyword 77 | #define GF256_ALIGNED __attribute__((aligned(16))) 78 | 79 | // Compiler-specific SSE headers 80 | #include 81 | 82 | #endif 83 | 84 | #elif defined(USE_NEON) 85 | 86 | #include "sse2neon.h" 87 | 88 | // Compiler-specific 128-bit SIMD register keyword 89 | #define GF256_M128 __m128i 90 | 91 | // Compiler-specific C++11 restrict keyword 92 | #define GF256_RESTRICT_KW __restrict__ 93 | 94 | // Compiler-specific force inline keyword 95 | #define GF256_FORCE_INLINE __attribute__((always_inline)) inline 96 | 97 | // Compiler-specific alignment keyword 98 | #define GF256_ALIGNED __attribute__((aligned(16))) 99 | 100 | #endif 101 | 102 | #if defined(NO_RESTRICT) 103 | #define GF256_RESTRICT 104 | #else 105 | #define GF256_RESTRICT GF256_RESTRICT_KW 106 | #endif 107 | 108 | //----------------------------------------------------------------------------- 109 | // GF(256) Context 110 | // 111 | // The context object stores tables required to perform library calculations. 112 | // 113 | // Usage Notes: 114 | // This struct should be aligned in memory, meaning that a pointer to it should 115 | // have the low 4 bits cleared. To achieve this simply tag the gf256_ctx object 116 | // with the GF256_ALIGNED macro provided above. 117 | 118 | #ifdef _MSC_VER 119 | #pragma warning(push) 120 | #pragma warning(disable: 4324) // warning C4324: 'gf256_ctx' : structure was padded due to __declspec(align()) 121 | #endif 122 | 123 | class CM256CC_API gf256_ctx // 141,072 bytes 124 | { 125 | public: 126 | gf256_ctx(); 127 | ~gf256_ctx(); 128 | 129 | bool isInitialized() const { return initialized; } 130 | 131 | /** Performs "x[] += y[]" bulk memory XOR operation */ 132 | static void gf256_add_mem(void * GF256_RESTRICT vx, const void * GF256_RESTRICT vy, int bytes); 133 | /** Performs "z[] += x[] + y[]" bulk memory operation */ 134 | static void gf256_add2_mem(void * GF256_RESTRICT vz, const void * GF256_RESTRICT vx, const void * GF256_RESTRICT vy, int bytes); 135 | /** Performs "z[] = x[] + y[]" bulk memory operation */ 136 | static void gf256_addset_mem(void * GF256_RESTRICT vz, const void * GF256_RESTRICT vx, const void * GF256_RESTRICT vy, int bytes); 137 | /** Swap two memory buffers in-place */ 138 | static void gf256_memswap(void * GF256_RESTRICT vx, void * GF256_RESTRICT vy, int bytes); 139 | 140 | // return x + y 141 | static GF256_FORCE_INLINE uint8_t gf256_add(const uint8_t x, const uint8_t y) 142 | { 143 | return x ^ y; 144 | } 145 | 146 | // return x * y 147 | // For repeated multiplication by a constant, it is faster to put the constant in y. 148 | GF256_FORCE_INLINE uint8_t gf256_mul(uint8_t x, uint8_t y) 149 | { 150 | return GF256_MUL_TABLE[((unsigned)y << 8) + x]; 151 | } 152 | 153 | // return x / y 154 | // Memory-access optimized for constant divisors in y. 155 | GF256_FORCE_INLINE uint8_t gf256_div(uint8_t x, uint8_t y) 156 | { 157 | return GF256_DIV_TABLE[((unsigned)y << 8) + x]; 158 | } 159 | 160 | // return 1 / x 161 | GF256_FORCE_INLINE uint8_t gf256_inv(uint8_t x) 162 | { 163 | return GF256_INV_TABLE[x]; 164 | } 165 | 166 | // This function generates each matrix element based on x_i, x_0, y_j 167 | // Note that for x_i == x_0, this will return 1, so it is better to unroll out the first row. 168 | GF256_FORCE_INLINE unsigned char getMatrixElement(const unsigned char x_i, const unsigned char x_0, const unsigned char y_j) 169 | { 170 | return gf256_div(gf256_add(y_j, x_0), gf256_add(x_i, y_j)); 171 | } 172 | 173 | /** Performs "z[] = x[] * y" bulk memory operation */ 174 | void gf256_mul_mem(void * GF256_RESTRICT vz, const void * GF256_RESTRICT vx, uint8_t y, int bytes); 175 | /** Performs "z[] += x[] * y" bulk memory operation */ 176 | void gf256_muladd_mem(void * GF256_RESTRICT vz, uint8_t y, const void * GF256_RESTRICT vx, int bytes); 177 | 178 | /** Performs "x[] /= y" bulk memory operation */ 179 | GF256_FORCE_INLINE void gf256_div_mem(void * GF256_RESTRICT vz, 180 | const void * GF256_RESTRICT vx, uint8_t y, int bytes) 181 | { 182 | gf256_mul_mem(vz, vx, GF256_INV_TABLE[y], bytes); // Multiply by inverse 183 | } 184 | 185 | // Polynomial used 186 | unsigned Polynomial; 187 | 188 | // Log/Exp tables 189 | uint16_t GF256_LOG_TABLE[256]; 190 | uint8_t GF256_EXP_TABLE[512 * 2 + 1]; 191 | 192 | // Mul/Div/Inv tables 193 | uint8_t GF256_MUL_TABLE[256 * 256]; 194 | uint8_t GF256_DIV_TABLE[256 * 256]; 195 | uint8_t GF256_INV_TABLE[256]; 196 | 197 | // Muladd_mem tables 198 | // We require memory to be aligned since the SIMD instructions benefit from 199 | // aligned accesses to the MM256_* table data. 200 | GF256_ALIGNED GF256_M128 MM256_TABLE_LO_Y[256]; 201 | GF256_ALIGNED GF256_M128 MM256_TABLE_HI_Y[256]; 202 | 203 | private: 204 | int gf256_init_(); 205 | 206 | void gf255_poly_init(int polynomialIndex); //!< Select which polynomial to use 207 | void gf256_explog_init(); //!< Construct EXP and LOG tables from polynomial 208 | void gf256_muldiv_init(); //!< Initialize MUL and DIV tables using LOG and EXP tables 209 | void gf256_inv_init(); //!< Initialize INV table using DIV table 210 | void gf256_muladd_mem_init(); //!< Initialize the MM256 tables using gf256_mul() 211 | 212 | static bool IsLittleEndian() 213 | { 214 | int x = 1; 215 | char *y = (char *) &x; 216 | 217 | return *y != 0; 218 | } 219 | 220 | //----------------------------------------------------------------------------- 221 | // Generator Polynomial 222 | 223 | // There are only 16 irreducible polynomials for GF(256) 224 | static const int GF256_GEN_POLY_COUNT = 16; 225 | static const uint8_t GF256_GEN_POLY[GF256_GEN_POLY_COUNT]; 226 | static const int DefaultPolynomialIndex = 3; 227 | 228 | bool initialized; 229 | }; 230 | 231 | #ifdef _MSC_VER 232 | #pragma warning(pop) 233 | #endif 234 | 235 | 236 | #endif // GF256_H 237 | -------------------------------------------------------------------------------- /libcm256cc.pc.in: -------------------------------------------------------------------------------- 1 | prefix=@CMAKE_INSTALL_PREFIX@ 2 | exec_prefix=${prefix} 3 | libdir=${exec_prefix}/@CMAKE_INSTALL_LIBDIR@ 4 | includedir=${prefix}/include 5 | 6 | Name: cm256cc library 7 | Description: Fast GF(256) Cauchy MDS Block Erasure Codec in C++ 8 | Version: @VERSION@ 9 | Cflags: -I${includedir}/ @CM256CC_PC_CFLAGS@ 10 | Libs: -L${libdir} -lcm256cc 11 | Libs.private: @CM256CC_PC_LIBS@ 12 | -------------------------------------------------------------------------------- /unit_test/UDPSocket.cpp: -------------------------------------------------------------------------------- 1 | /////////////////////////////////////////////////////////////////////////////////// 2 | // SDRdaemon - send I/Q samples read from a SDR device over the network via UDP. // 3 | // // 4 | // Copyright (C) 2015 Edouard Griffiths, F4EXB // 5 | // // 6 | // This program is free software; you can redistribute it and/or modify // 7 | // it under the terms of the GNU General Public License as published by // 8 | // the Free Software Foundation as version 3 of the License, or // 9 | // // 10 | // This program is distributed in the hope that it will be useful, // 11 | // but WITHOUT ANY WARRANTY; without even the implied warranty of // 12 | // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // 13 | // GNU General Public License V3 for more details. // 14 | // // 15 | // You should have received a copy of the GNU General Public License // 16 | // along with this program. If not, see . // 17 | /////////////////////////////////////////////////////////////////////////////////// 18 | 19 | // Original code is posted at: https://cppcodetips.wordpress.com/2014/01/29/udp-socket-class-in-c/ 20 | 21 | #include "UDPSocket.h" 22 | #include 23 | #include 24 | #include 25 | #include 26 | #include 27 | #include 28 | #include 29 | #include 30 | #include 31 | 32 | CSocketException::CSocketException( const string &sMessage, bool blSysMsg /*= false*/ ) : m_sMsg(sMessage) 33 | { 34 | if (blSysMsg) { 35 | m_sMsg.append(": "); 36 | m_sMsg.append(strerror(errno)); 37 | } 38 | } 39 | 40 | CSocketException::~CSocketException() throw() 41 | { 42 | 43 | } 44 | 45 | CSocket::~CSocket() 46 | { 47 | ::close(m_sockDesc); 48 | m_sockDesc = -1; 49 | } 50 | 51 | CSocket::CSocket( SocketType type, NetworkLayerProtocol protocol ) : m_sockDesc(-1) 52 | { 53 | m_sockDesc = socket(protocol, type, 0); 54 | if (m_sockDesc < 0) 55 | { 56 | throw CSocketException("Socket creation failed (socket())", true); 57 | } 58 | } 59 | 60 | CSocket::CSocket( int sockDesc ) 61 | { 62 | m_sockDesc = sockDesc; 63 | } 64 | 65 | CSocket::CSocket( const CSocket &sock __attribute__((unused))) 66 | { 67 | 68 | } 69 | 70 | void CSocket::operator=( const CSocket &sock __attribute__((unused))) 71 | { 72 | 73 | } 74 | 75 | std::string CSocket::GetLocalAddress() 76 | { 77 | sockaddr_in addr; 78 | unsigned int addr_len = sizeof(addr); 79 | 80 | if (getsockname(m_sockDesc, (sockaddr *) &addr, (socklen_t *) &addr_len) < 0) { 81 | throw CSocketException("Fetch of local address failed (getsockname())", true); 82 | } 83 | return inet_ntoa(addr.sin_addr); 84 | } 85 | 86 | unsigned short CSocket::GetLocalPort() 87 | { 88 | sockaddr_in addr; 89 | unsigned int addr_len = sizeof(addr); 90 | 91 | if (getsockname(m_sockDesc, (sockaddr *) &addr, (socklen_t *) &addr_len) < 0) { 92 | throw CSocketException("Fetch of local port failed (getsockname())", true); 93 | } 94 | return ntohs(addr.sin_port); 95 | } 96 | 97 | void CSocket::BindLocalPort( unsigned short localPort ) 98 | { 99 | // Bind the socket to its port 100 | sockaddr_in localAddr; 101 | memset(&localAddr, 0, sizeof(localAddr)); 102 | localAddr.sin_family = AF_INET; 103 | localAddr.sin_addr.s_addr = htonl(INADDR_ANY); 104 | localAddr.sin_port = htons(localPort); 105 | 106 | if (::bind(m_sockDesc, (sockaddr *) &localAddr, sizeof(sockaddr_in)) < 0) { 107 | throw CSocketException("Set of local port failed (bind())", true); 108 | } 109 | } 110 | 111 | void CSocket::BindLocalAddressAndPort( const string &localAddress, unsigned short localPort /*= 0*/ ) 112 | { 113 | // Get the address of the requested host 114 | sockaddr_in localAddr; 115 | FillAddr(localAddress, localPort, localAddr); 116 | 117 | if (::bind(m_sockDesc, (sockaddr *) &localAddr, sizeof(sockaddr_in)) < 0) { 118 | throw CSocketException("Set of local address and port failed (bind())", true); 119 | } 120 | } 121 | 122 | void CSocket::FillAddr( const string & localAddress, unsigned short localPort, sockaddr_in& localAddr ) 123 | { 124 | ////cout<<"\n Inside Fille addr:"<h_addr_list[0]); 135 | 136 | localAddr.sin_port = htons(localPort); // Assign port in network byte order 137 | ////cout<<"\n returning from Fille addr"; 138 | } 139 | 140 | unsigned long int CSocket::GetReadBufferSize() 141 | { 142 | unsigned long int nSize; 143 | socklen_t n = sizeof(nSize); 144 | getsockopt(m_sockDesc,SOL_SOCKET,SO_RCVBUF,(void *)&nSize, (&n)); 145 | // now the variable nSize will have the socket size 146 | return nSize; 147 | } 148 | 149 | void CSocket::SetReadBufferSize( unsigned int nSize ) 150 | { 151 | if (setsockopt(m_sockDesc, SOL_SOCKET, SO_RCVBUF, &nSize, sizeof(nSize)) == -1) 152 | { 153 | throw CSocketException("Error in setting socket buffer size ", true); 154 | } 155 | } 156 | 157 | void CSocket::SetNonBlocking( bool bBlocking ) 158 | { 159 | int opts; 160 | 161 | opts = fcntl ( m_sockDesc, F_GETFL ); 162 | 163 | if ( opts < 0 ) 164 | { 165 | return; 166 | } 167 | 168 | if ( bBlocking ) 169 | opts = ( opts | O_NONBLOCK ); 170 | else 171 | opts = ( opts & ~O_NONBLOCK ); 172 | 173 | fcntl ( m_sockDesc, F_SETFL,opts ); 174 | } 175 | 176 | void CSocket::ConnectToHost( const string &foreignAddress, unsigned short foreignPort ) 177 | { 178 | //cout<<"\nstart Connect to host"; 179 | // Get the address of the requested host 180 | sockaddr_in destAddr; 181 | //cout<<"\ninside Connect to host"; 182 | FillAddr(foreignAddress, foreignPort, destAddr); 183 | 184 | //cout<<"trying to connect to host"; 185 | // Try to connect to the given port 186 | if (::connect(m_sockDesc, (sockaddr *) &destAddr, sizeof(destAddr)) < 0) { 187 | throw CSocketException("Connect failed (connect())", true); 188 | } 189 | //cout<<"\n after connecting"; 190 | 191 | } 192 | 193 | void CSocket::Send( const void *buffer, int bufferLen ) 194 | { 195 | if (::send(m_sockDesc, (void *) buffer, bufferLen, 0) < 0) { 196 | throw CSocketException("Send failed (send())", true); 197 | } 198 | } 199 | 200 | int CSocket::Recv( void *buffer, int bufferLen ) 201 | { 202 | int nBytes; 203 | if ((nBytes = ::recv(m_sockDesc, (void *) buffer, bufferLen, 0)) < 0) { 204 | throw CSocketException("Received failed (recv())", true); 205 | } 206 | char* sData = static_cast(buffer); 207 | sData[nBytes] = '\0'; 208 | return nBytes; 209 | } 210 | 211 | std::string CSocket::GetPeerAddress() 212 | { 213 | sockaddr_in addr; 214 | unsigned int addr_len = sizeof(addr); 215 | 216 | if (getpeername(m_sockDesc, (sockaddr *) &addr,(socklen_t *) &addr_len) < 0) { 217 | throw CSocketException("Fetch of foreign address failed (getpeername())", true); 218 | } 219 | return inet_ntoa(addr.sin_addr); 220 | } 221 | 222 | unsigned short CSocket::GetPeerPort() 223 | { 224 | sockaddr_in addr; 225 | unsigned int addr_len = sizeof(addr); 226 | 227 | if (getpeername(m_sockDesc, (sockaddr *) &addr, (socklen_t *) &addr_len) < 0) { 228 | throw CSocketException("Fetch of foreign port failed (getpeername())", true); 229 | } 230 | return ntohs(addr.sin_port); 231 | } 232 | 233 | CSocket& CSocket::operator<<(const string& sStr ) 234 | { 235 | Send(sStr.c_str(), sStr.length()); 236 | return *this; 237 | } 238 | 239 | CSocket& CSocket::operator>>( string& sStr ) 240 | { 241 | char *buff = new char[GetReadBufferSize()]; 242 | Recv(buff, GetReadBufferSize()); 243 | sStr.append(buff); 244 | delete [] buff; 245 | return *this; 246 | } 247 | 248 | int CSocket::OnDataRead(unsigned long timeToWait) 249 | { 250 | /* master file descriptor list */ 251 | fd_set master; 252 | //struct timeval *ptimeout = NULL; 253 | 254 | /* temp file descriptor list for select() */ 255 | fd_set read_fds; 256 | 257 | /* maximum file descriptor number */ 258 | int fdmax; 259 | 260 | /* clear the master and temp sets */ 261 | FD_ZERO(&master); 262 | FD_ZERO(&read_fds); 263 | 264 | /* add the listener to the master set */ 265 | FD_SET(m_sockDesc, &master); 266 | /* keep track of the biggest file descriptor */ 267 | fdmax = m_sockDesc; /* so far, it's this one*/ 268 | 269 | /* copy it */ 270 | read_fds = master; 271 | //cout<<"Waiting for select"; 272 | int nRet; 273 | if (timeToWait == ULONG_MAX) 274 | { 275 | nRet = select(fdmax+1, &read_fds, NULL, NULL, NULL); 276 | if (nRet == -1) 277 | nRet = DATA_EXCEPTION; 278 | else if (nRet > 0) 279 | nRet = DATA_ARRIVED; 280 | } 281 | else 282 | { 283 | struct timeval timeout; 284 | timeout.tv_sec = timeToWait; 285 | timeout.tv_usec = 0; 286 | nRet = select(fdmax+1, &read_fds, NULL, NULL, &timeout); 287 | if (nRet == -1) 288 | nRet = DATA_EXCEPTION; 289 | else if (nRet > 0) 290 | nRet = DATA_ARRIVED; 291 | else if(nRet == 0) 292 | nRet = DATA_TIMED_OUT; 293 | } 294 | 295 | return nRet; 296 | } 297 | 298 | void CSocket::SetBindToDevice( const string& sInterface ) 299 | { 300 | struct ifreq ifr; 301 | memset(&ifr, 0, sizeof(ifr)); 302 | snprintf(ifr.ifr_name, sizeof(ifr.ifr_name), "%s", sInterface.c_str()); 303 | //Todo:SO_BINDTODEVICE not declared error comes in CygWin, need to compile in Linux. 304 | /*int nRet = ::setsockopt(m_sockDesc, SOL_SOCKET, SO_BINDTODEVICE, (void*)&ifr, sizeof(ifr)); 305 | 306 | if (nRet < 0) 307 | { 308 | throw CSocketException("Error in binding to device ", true); 309 | }*/ 310 | } 311 | 312 | UDPSocket::UDPSocket() : CSocket(UdpSocket,IPv4Protocol) 313 | { 314 | SetBroadcast(); 315 | } 316 | 317 | UDPSocket::UDPSocket( unsigned short localPort ) : 318 | CSocket(UdpSocket,IPv4Protocol) 319 | { 320 | BindLocalPort(localPort); 321 | SetBroadcast(); 322 | } 323 | 324 | UDPSocket::UDPSocket( const string &localAddress, unsigned short localPort ) : 325 | CSocket(UdpSocket,IPv4Protocol) 326 | { 327 | BindLocalAddressAndPort(localAddress, localPort); 328 | SetBroadcast(); 329 | } 330 | 331 | void UDPSocket::DisconnectFromHost() 332 | { 333 | sockaddr_in nullAddr; 334 | memset(&nullAddr, 0, sizeof(nullAddr)); 335 | nullAddr.sin_family = AF_UNSPEC; 336 | // Try to disconnect 337 | if (::connect(m_sockDesc, (sockaddr *) &nullAddr, sizeof(nullAddr)) < 0) 338 | { 339 | if (errno != EAFNOSUPPORT) 340 | { 341 | throw CSocketException("Disconnect failed (connect())", true); 342 | } 343 | } 344 | } 345 | 346 | void UDPSocket::SendDataGram( const void *buffer, int bufferLen, const string &foreignAddress, 347 | unsigned short foreignPort ) 348 | { 349 | //cout<<"Befor Fill addr"; 350 | sockaddr_in destAddr; 351 | FillAddr(foreignAddress, foreignPort, destAddr); 352 | //cout<<"Befor socket send"; 353 | // Write out the whole buffer as a single message. 354 | if (sendto(m_sockDesc, (void *) buffer, bufferLen, 0,(sockaddr *) &destAddr, sizeof(destAddr)) != bufferLen) 355 | { 356 | throw CSocketException("Send failed (sendto())", true); 357 | } 358 | 359 | } 360 | 361 | int UDPSocket::RecvDataGram( void *buffer, int bufferLen, string &sourceAddress, unsigned short &sourcePort ) 362 | { 363 | sockaddr_in clntAddr; 364 | socklen_t addrLen = sizeof(clntAddr); 365 | int nBytes; 366 | if ((nBytes = recvfrom(m_sockDesc, (void *) buffer, bufferLen, 0, (sockaddr *) &clntAddr, 367 | (socklen_t *) &addrLen)) < 0) 368 | { 369 | throw CSocketException("Receive failed (recvfrom())", true); 370 | } 371 | sourceAddress = inet_ntoa(clntAddr.sin_addr); 372 | sourcePort = ntohs(clntAddr.sin_port); 373 | char* sData = static_cast(buffer); 374 | sData[nBytes] = '\0'; 375 | return nBytes; 376 | } 377 | 378 | void UDPSocket::SetMulticastTTL( unsigned char multicastTTL ) 379 | { 380 | if (setsockopt(m_sockDesc, IPPROTO_IP, IP_MULTICAST_TTL, (void *) &multicastTTL, sizeof(multicastTTL)) < 0) 381 | { 382 | throw CSocketException("Multicast TTL set failed (setsockopt())", true); 383 | } 384 | } 385 | 386 | void UDPSocket::JoinGroup( const string &multicastGroup ) 387 | { 388 | struct ip_mreq multicastRequest; 389 | 390 | multicastRequest.imr_multiaddr.s_addr = inet_addr(multicastGroup.c_str()); 391 | multicastRequest.imr_interface.s_addr = htonl(INADDR_ANY); 392 | if (setsockopt(m_sockDesc, IPPROTO_IP, IP_ADD_MEMBERSHIP, 393 | (void *) &multicastRequest, 394 | sizeof(multicastRequest)) < 0) 395 | { 396 | throw CSocketException("Multicast group join failed (setsockopt())", true); 397 | } 398 | 399 | } 400 | 401 | void UDPSocket::LeaveGroup( const string &multicastGroup ) 402 | { 403 | struct ip_mreq multicastRequest; 404 | 405 | multicastRequest.imr_multiaddr.s_addr = inet_addr(multicastGroup.c_str()); 406 | multicastRequest.imr_interface.s_addr = htonl(INADDR_ANY); 407 | if (setsockopt(m_sockDesc, IPPROTO_IP, IP_DROP_MEMBERSHIP, 408 | (void *) &multicastRequest, 409 | sizeof(multicastRequest)) < 0) 410 | { 411 | throw CSocketException("Multicast group leave failed (setsockopt())", true); 412 | } 413 | 414 | } 415 | 416 | void UDPSocket::SetBroadcast() 417 | { 418 | // If this fails, we'll hear about it when we try to send. This will allow 419 | // system that cannot broadcast to continue if they don't plan to broadcast 420 | int broadcastPermission = 1; 421 | setsockopt(m_sockDesc, SOL_SOCKET, SO_BROADCAST, 422 | (void *) &broadcastPermission, sizeof(broadcastPermission)); 423 | 424 | } 425 | 426 | 427 | -------------------------------------------------------------------------------- /unit_test/UDPSocket.h: -------------------------------------------------------------------------------- 1 | /////////////////////////////////////////////////////////////////////////////////// 2 | // SDRdaemon - send I/Q samples read from a SDR device over the network via UDP. // 3 | // // 4 | // Copyright (C) 2015 Edouard Griffiths, F4EXB // 5 | // // 6 | // This program is free software; you can redistribute it and/or modify // 7 | // it under the terms of the GNU General Public License as published by // 8 | // the Free Software Foundation as version 3 of the License, or // 9 | // // 10 | // This program is distributed in the hope that it will be useful, // 11 | // but WITHOUT ANY WARRANTY; without even the implied warranty of // 12 | // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // 13 | // GNU General Public License V3 for more details. // 14 | // // 15 | // You should have received a copy of the GNU General Public License // 16 | // along with this program. If not, see . // 17 | /////////////////////////////////////////////////////////////////////////////////// 18 | 19 | // Original code is posted at: https://cppcodetips.wordpress.com/2014/01/29/udp-socket-class-in-c/ 20 | 21 | #ifndef INCLUDE_UDPSOCKET_H_ 22 | #define INCLUDE_UDPSOCKET_H_ 23 | 24 | #include // For string 25 | #include // For exception class 26 | #include 27 | #include // For data types 28 | #include // For socket(), connect(), send(), and recv() 29 | #include // For gethostbyname() 30 | #include // For inet_addr() 31 | #include // For close() 32 | #include // For sockaddr_in 33 | #include 34 | #include 35 | 36 | using namespace std; 37 | 38 | /** 39 | * Signals a problem with the execution of a socket call. 40 | */ 41 | 42 | class CSocketException: public std::exception 43 | { 44 | public: 45 | /** 46 | * Construct a SocketException with a explanatory message. 47 | * @param message explanatory message 48 | * @param bSysMsg true if system message (from strerror(errno)) 49 | * should be postfixed to the user provided message 50 | */ 51 | CSocketException(const string &message, bool bSysMsg = false); 52 | 53 | 54 | /** Destructor. 55 | * Virtual to allow for subclassing. 56 | */ 57 | virtual ~CSocketException() throw (); 58 | 59 | /** Returns a pointer to the (constant) error description. 60 | * @return A pointer to a \c const \c char*. The underlying memory 61 | * is in posession of the \c Exception object. Callers \a must 62 | * not attempt to free the memory. 63 | */ 64 | virtual const char* what() const throw (){ return m_sMsg.c_str(); } 65 | 66 | protected: 67 | /** Error message. 68 | */ 69 | std::string m_sMsg; 70 | }; 71 | 72 | /** 73 | * Base class representing basic communication endpoint. 74 | */ 75 | 76 | class CSocket 77 | { 78 | public: 79 | virtual ~CSocket(); 80 | 81 | /** 82 | * Enum to represent type of socket(UDP or TCP) 83 | */ 84 | enum SocketType 85 | { 86 | TcpSocket = SOCK_STREAM, 87 | UdpSocket = SOCK_DGRAM, 88 | UnknownSocketType =-1 89 | }; 90 | 91 | /** 92 | * Enum to represent type network layer protocol used for socket 93 | */ 94 | enum NetworkLayerProtocol 95 | { 96 | IPv4Protocol = AF_INET, 97 | IPv6Protocol = AF_INET6, 98 | UnknownNetworkLayerProtocol = -1 99 | }; 100 | 101 | /** 102 | * Enum to represent Wait Result when reading data from a socket 103 | */ 104 | enum ReadResult 105 | { 106 | DATA_ARRIVED = 0, 107 | DATA_TIMED_OUT = ETIMEDOUT, 108 | DATA_EXCEPTION = 255 109 | }; 110 | 111 | /** 112 | * Get the local address 113 | * @return local address of socket 114 | * @exception CSocketException thrown if fetch fails 115 | */ 116 | 117 | string GetLocalAddress(); 118 | 119 | /** 120 | * Get the local port 121 | * @return local port of socket 122 | * @exception CSocketException thrown if fetch fails 123 | */ 124 | 125 | unsigned short GetLocalPort(); 126 | 127 | 128 | /** 129 | * Set the local port to the specified port and the local address 130 | * to any interface 131 | * @param localPort local port 132 | * @exception CSocketException thrown if setting local port fails 133 | */ 134 | 135 | void BindLocalPort(unsigned short localPort); 136 | 137 | /** 138 | * Set the local port to the specified port and the local address 139 | * to the specified address. If you omit the port, a random port 140 | * will be selected. 141 | * @param localAddress local address 142 | * @param localPort local port 143 | * @exception CSocketException thrown if setting local port or address fails 144 | */ 145 | 146 | void BindLocalAddressAndPort(const string &localAddress, unsigned short localPort = 0); 147 | 148 | /** 149 | * Returns the size of the internal read buffer. This limits the amount of data that the client 150 | * can receive before you call 151 | */ 152 | unsigned long int GetReadBufferSize (); 153 | 154 | /** 155 | * Sets the read buffer size of the socket. 156 | * @param Size of the buffer. 157 | */ 158 | void SetReadBufferSize(unsigned int nSize); 159 | 160 | /** 161 | * Sets the socket to Blocking/Non blocking state. 162 | * @param Bool flag for Non blocking status. 163 | */ 164 | void SetNonBlocking(bool bBlocking); 165 | 166 | /** 167 | * Establish a socket connection with the given foreign 168 | * address and port 169 | * @param foreignAddress foreign address (IP address or name) 170 | * @param foreignPort foreign port 171 | * @exception SocketException thrown if unable to establish connection 172 | */ 173 | void ConnectToHost(const string &foreignAddress, unsigned short foreignPort); 174 | 175 | /** 176 | * Write the given buffer to this socket. Call connect() before 177 | * calling send() 178 | * @param buffer buffer to be written 179 | * @param bufferLen number of bytes from buffer to be written 180 | * @exception SocketException thrown if unable to send data 181 | */ 182 | void Send(const void *buffer, int bufferLen); 183 | 184 | /** 185 | * Read into the given buffer up to bufferLen bytes data from this 186 | * socket. Call connect() before calling recv() 187 | * @param buffer buffer to receive the data 188 | * @param bufferLen maximum number of bytes to read into buffer 189 | * @return number of bytes read, 0 for EOF, and -1 for error 190 | * @exception SocketException thrown if unable to receive data 191 | */ 192 | int Recv(void *buffer, int bufferLen); 193 | 194 | /** 195 | * Get the foreign address. Call connect() before calling recv() 196 | * @return foreign address 197 | * @exception SocketException thrown if unable to fetch foreign address 198 | */ 199 | string GetPeerAddress(); 200 | 201 | /** 202 | * Get the foreign port. Call connect() before calling recv() 203 | * @return foreign port 204 | * @exception SocketException thrown if unable to fetch foreign port 205 | */ 206 | unsigned short GetPeerPort(); 207 | 208 | /** 209 | * Writing sStr to socket 210 | */ 211 | CSocket& operator<<(const string& sStr ); 212 | 213 | /** 214 | * Reading data to sStr from socket 215 | */ 216 | CSocket& operator>>(string& sStr); 217 | 218 | /** 219 | * Blocking function to check whether data arrived in socket for reading. 220 | * @param timeToWait waits for 'timeToWait' seconds. 221 | */ 222 | virtual int OnDataRead(unsigned long timeToWait = ULONG_MAX); 223 | 224 | /** 225 | * To Bind socket to a symbolic device name like eth0 226 | * @param sInterface NIC device name 227 | */ 228 | void SetBindToDevice(const string& sInterface); 229 | 230 | protected: 231 | /** 232 | * Internal Socket descriptor 233 | **/ 234 | int m_sockDesc; 235 | 236 | CSocket(SocketType type, NetworkLayerProtocol protocol); 237 | CSocket(int sockDesc); 238 | static void FillAddr( const string & localAddress, unsigned short localPort, sockaddr_in& localAddr); 239 | 240 | private: 241 | // Prevent the user from trying to use Exact copy of this object 242 | CSocket(const CSocket &sock); 243 | void operator=(const CSocket &sock); 244 | }; 245 | 246 | /** 247 | * UDP Socket class. 248 | */ 249 | 250 | class UDPSocket : public CSocket 251 | { 252 | public: 253 | /** 254 | * Construct a UDP socket 255 | * @exception SocketException thrown if unable to create UDP socket 256 | */ 257 | UDPSocket(); 258 | /** 259 | * Construct a UDP socket with the given local port 260 | * @param localPort local port 261 | * @exception SocketException thrown if unable to create UDP socket 262 | */ 263 | UDPSocket(unsigned short localPort); 264 | 265 | /** 266 | * Construct a UDP socket with the given local port and address 267 | * @param localAddress local address 268 | * @param localPort local port 269 | * @exception SocketException thrown if unable to create UDP socket 270 | */ 271 | UDPSocket(const string &localAddress, unsigned short localPort); 272 | 273 | /** 274 | * Unset foreign address and port 275 | * @return true if disassociation is successful 276 | * @exception SocketException thrown if unable to disconnect UDP socket 277 | */ 278 | 279 | /** 280 | * Unset foreign address and port 281 | * @return true if disassociation is successful 282 | * @exception SocketException thrown if unable to disconnect UDP socket 283 | */ 284 | void DisconnectFromHost(); 285 | 286 | /** 287 | * Send the given buffer as a UDP datagram to the 288 | * specified address/port 289 | * @param buffer buffer to be written 290 | * @param bufferLen number of bytes to write 291 | * @param foreignAddress address (IP address or name) to send to 292 | * @param foreignPort port number to send to 293 | * @return true if send is successful 294 | * @exception SocketException thrown if unable to send datagram 295 | */ 296 | void SendDataGram(const void *buffer, int bufferLen, const string &foreignAddress, 297 | unsigned short foreignPort); 298 | 299 | /** 300 | * Read read up to bufferLen bytes data from this socket. The given buffer 301 | * is where the data will be placed 302 | * @param buffer buffer to receive data 303 | * @param bufferLen maximum number of bytes to receive 304 | * @param sourceAddress address of datagram source 305 | * @param sourcePort port of data source 306 | * @return number of bytes received and -1 for error 307 | * @exception SocketException thrown if unable to receive datagram 308 | */ 309 | int RecvDataGram(void *buffer, int bufferLen, string &sourceAddress, 310 | unsigned short &sourcePort); 311 | 312 | /** 313 | * Set the multicast TTL 314 | * @param multicastTTL multicast TTL 315 | * @exception SocketException thrown if unable to set TTL 316 | */ 317 | void SetMulticastTTL(unsigned char multicastTTL); 318 | 319 | /** 320 | * Join the specified multicast group 321 | * @param multicastGroup multicast group address to join 322 | * @exception SocketException thrown if unable to join group 323 | */ 324 | void JoinGroup(const string &multicastGroup); 325 | 326 | /** 327 | * Leave the specified multicast group 328 | * @param multicastGroup multicast group address to leave 329 | * @exception SocketException thrown if unable to leave group 330 | */ 331 | void LeaveGroup(const string &multicastGroup); 332 | 333 | private: 334 | void SetBroadcast(); 335 | 336 | }; 337 | 338 | 339 | #endif /* INCLUDE_UDPSOCKET_H_ */ 340 | -------------------------------------------------------------------------------- /unit_test/cm256_test.cpp: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright (c) 2015 Christopher A. Taylor. All rights reserved. 3 | 4 | Redistribution and use in source and binary forms, with or without 5 | modification, are permitted provided that the following conditions are met: 6 | 7 | * Redistributions of source code must retain the above copyright notice, 8 | this list of conditions and the following disclaimer. 9 | * Redistributions in binary form must reproduce the above copyright notice, 10 | this list of conditions and the following disclaimer in the documentation 11 | and/or other materials provided with the distribution. 12 | * Neither the name of CM256 nor the names of its contributors may be 13 | used to endorse or promote products derived from this software without 14 | specific prior written permission. 15 | 16 | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" 17 | AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 18 | IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 19 | ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE 20 | LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR 21 | CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF 22 | SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS 23 | INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN 24 | CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) 25 | ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE 26 | POSSIBILITY OF SUCH DAMAGE. 27 | */ 28 | 29 | #include 30 | #include 31 | 32 | #include "../cm256.h" 33 | 34 | long long getUSecs() 35 | { 36 | struct timeval tp; 37 | gettimeofday(&tp, 0); 38 | return (long long) tp.tv_sec * 1000000L + tp.tv_usec; 39 | } 40 | 41 | void initializeBlocks(CM256::cm256_block originals[256], int blockCount, int blockBytes) 42 | { 43 | for (int i = 0; i < blockCount; ++i) 44 | { 45 | for (int j = 0; j < blockBytes; ++j) 46 | { 47 | const uint8_t expected = (uint8_t)(i + j * 13); 48 | uint8_t* data = (uint8_t*)originals[i].Block; 49 | data[j] = expected; 50 | } 51 | } 52 | } 53 | 54 | bool validateSolution(CM256::cm256_block_t* blocks, int blockCount, int blockBytes) 55 | { 56 | uint8_t seen[256] = { 0 }; 57 | 58 | for (int i = 0; i < blockCount; ++i) 59 | { 60 | uint8_t index = blocks[i].Index; 61 | 62 | if (index >= blockCount) 63 | { 64 | return false; 65 | } 66 | 67 | if (seen[index]) 68 | { 69 | return false; 70 | } 71 | 72 | seen[index] = 1; 73 | 74 | for (int j = 0; j < blockBytes; ++j) 75 | { 76 | const uint8_t expected = (uint8_t)(index + j * 13); 77 | uint8_t* blockData = (uint8_t*)blocks[i].Block; 78 | if (blockData[j] != expected) 79 | { 80 | return false; 81 | } 82 | } 83 | } 84 | 85 | return true; 86 | } 87 | 88 | 89 | 90 | bool ExampleFileUsage() 91 | { 92 | CM256 cm256; 93 | 94 | if (!cm256.isInitialized()) 95 | { 96 | return false; 97 | } 98 | 99 | CM256::cm256_encoder_params params; 100 | 101 | // Number of bytes per file block 102 | params.BlockBytes = 1296; 103 | 104 | // Number of blocks 105 | params.OriginalCount = 100; 106 | 107 | // Number of additional recovery blocks generated by encoder 108 | params.RecoveryCount = 30; 109 | 110 | // Size of the original file 111 | static const int OriginalFileBytes = params.OriginalCount * params.BlockBytes; 112 | 113 | // Allocate and fill the original file data 114 | uint8_t* originalFileData = new uint8_t[OriginalFileBytes]; 115 | for (int i = 0; i < OriginalFileBytes; ++i) 116 | { 117 | originalFileData[i] = (uint8_t)i; 118 | } 119 | 120 | // Pointers to data 121 | CM256::cm256_block blocks[256]; 122 | for (int i = 0; i < params.OriginalCount; ++i) 123 | { 124 | blocks[i].Block = originalFileData + i * params.BlockBytes; 125 | } 126 | 127 | // Recovery data 128 | uint8_t* recoveryBlocks = new uint8_t[params.RecoveryCount * params.BlockBytes]; 129 | 130 | // Generate recovery data 131 | if (cm256.cm256_encode(params, blocks, recoveryBlocks)) 132 | { 133 | delete[] originalFileData; 134 | delete[] recoveryBlocks; 135 | return false; 136 | } 137 | 138 | // Initialize the indices 139 | for (int i = 0; i < params.OriginalCount; ++i) 140 | { 141 | blocks[i].Index = CM256::cm256_get_original_block_index(params, i); 142 | } 143 | 144 | //// Simulate loss of data, substituting a recovery block in its place //// 145 | for (int i = 0; i < params.RecoveryCount && i < params.OriginalCount; ++i) 146 | { 147 | blocks[i].Block = recoveryBlocks + params.BlockBytes * i; // First recovery block 148 | blocks[i].Index = CM256::cm256_get_recovery_block_index(params, i); // First recovery block index 149 | } 150 | //// Simulate loss of data, substituting a recovery block in its place //// 151 | 152 | if (cm256.cm256_decode(params, blocks)) 153 | { 154 | delete[] originalFileData; 155 | delete[] recoveryBlocks; 156 | return false; 157 | } 158 | 159 | for (int i = 0; i < params.RecoveryCount && i < params.OriginalCount; ++i) 160 | { 161 | uint8_t* block = (uint8_t*)blocks[i].Block; 162 | int index = blocks[i].Index; 163 | 164 | for (int j = 0; j < params.BlockBytes; ++j) 165 | { 166 | const uint8_t expected = (uint8_t)(j + index * params.BlockBytes); 167 | if (block[j] != expected) 168 | { 169 | delete[] originalFileData; 170 | delete[] recoveryBlocks; 171 | return false; 172 | } 173 | } 174 | } 175 | 176 | delete[] originalFileData; 177 | delete[] recoveryBlocks; 178 | 179 | return true; 180 | } 181 | 182 | bool example2() 183 | { 184 | static const int payloadSize = 256; // represents 4 subframes of 64 bytes 185 | #pragma pack(push, 1) 186 | struct ProtectedBlock 187 | { 188 | uint8_t blockIndex; 189 | uint8_t data[payloadSize]; 190 | }; 191 | struct SuperBlock 192 | { 193 | uint8_t frameIndex; 194 | uint8_t blockIndex; 195 | ProtectedBlock protectedBlock; 196 | }; 197 | #pragma pack(pop) 198 | 199 | CM256 cm256; 200 | if (!cm256.isInitialized()) 201 | { 202 | return false; 203 | } 204 | 205 | CM256::cm256_encoder_params params; 206 | 207 | // Number of bytes per file block 208 | params.BlockBytes = sizeof(ProtectedBlock); 209 | 210 | // Number of blocks 211 | params.OriginalCount = 128; // Superframe = set of protected frames 212 | 213 | // Number of additional recovery blocks generated by encoder 214 | params.RecoveryCount = 32; 215 | 216 | SuperBlock* txBuffer = new SuperBlock[params.OriginalCount+params.RecoveryCount]; 217 | ProtectedBlock* txRecovery = new ProtectedBlock[params.RecoveryCount]; 218 | CM256::cm256_block *txDescriptorBlocks = new CM256::cm256_block[params.OriginalCount+params.RecoveryCount]; 219 | int frameCount = 0; 220 | 221 | // Fill original data 222 | for (int i = 0; i < params.OriginalCount+params.RecoveryCount; ++i) 223 | { 224 | txBuffer[i].frameIndex = frameCount; 225 | txBuffer[i].blockIndex = i; 226 | 227 | if (i < params.OriginalCount) 228 | { 229 | txBuffer[i].protectedBlock.blockIndex = i; 230 | 231 | for (int j = 0; j < payloadSize; ++j) 232 | { 233 | txBuffer[i].protectedBlock.data[j] = i; 234 | } 235 | 236 | txDescriptorBlocks[i].Block = (void *) &txBuffer[i].protectedBlock; 237 | txDescriptorBlocks[i].Index = txBuffer[i].blockIndex; 238 | } 239 | else 240 | { 241 | memset((void *) &txBuffer[i].protectedBlock, 0, sizeof(ProtectedBlock)); 242 | txDescriptorBlocks[i].Block = (void *) &txBuffer[i].protectedBlock; 243 | txDescriptorBlocks[i].Index = i; 244 | } 245 | } 246 | 247 | // Generate recovery data 248 | 249 | long long ts = getUSecs(); 250 | 251 | if (cm256.cm256_encode(params, txDescriptorBlocks, txRecovery)) 252 | { 253 | std::cerr << "example2: encode failed" << std::endl; 254 | delete[] txBuffer; 255 | delete[] txRecovery; 256 | return false; 257 | } 258 | 259 | long long usecs = getUSecs() - ts; 260 | 261 | std::cerr << "Encoded in " << usecs << " microseconds" << std::endl; 262 | 263 | // insert recovery data in sent data 264 | for (int i = 0; i < params.RecoveryCount; i++) 265 | { 266 | txBuffer[params.OriginalCount+i].protectedBlock = txRecovery[i]; 267 | } 268 | 269 | SuperBlock* rxBuffer = new SuperBlock[params.OriginalCount]; 270 | CM256::cm256_block rxDescriptorBlocks[params.OriginalCount]; 271 | int k = 0; 272 | 273 | for (int i = 0; i < params.OriginalCount+params.RecoveryCount; i++) 274 | { 275 | if (k < params.OriginalCount) 276 | { 277 | if (i % 5 != 4) 278 | { 279 | rxBuffer[k] = txBuffer[i]; 280 | rxDescriptorBlocks[k].Block = (void *) &rxBuffer[k].protectedBlock; 281 | rxDescriptorBlocks[k].Index = rxBuffer[k].blockIndex; 282 | k++; 283 | } 284 | } 285 | } 286 | 287 | ts = getUSecs(); 288 | 289 | if (cm256.cm256_decode(params, rxDescriptorBlocks)) 290 | { 291 | delete[] txBuffer; 292 | delete[] txRecovery; 293 | delete[] rxBuffer; 294 | 295 | return false; 296 | } 297 | 298 | usecs = getUSecs() - ts; 299 | 300 | for (int i = 0; i < params.OriginalCount; i++) 301 | { 302 | std::cerr << i << ":" 303 | << (unsigned int) rxBuffer[i].blockIndex << ":" 304 | << (unsigned int) rxBuffer[i].protectedBlock.blockIndex << ":" 305 | << (unsigned int) rxBuffer[i].protectedBlock.data[0] << std::endl; 306 | } 307 | 308 | std::cerr << "Decoded in " << usecs << " microseconds" << std::endl; 309 | 310 | delete[] txDescriptorBlocks; 311 | delete[] txBuffer; 312 | delete[] txRecovery; 313 | delete[] rxBuffer; 314 | 315 | return true; 316 | } 317 | 318 | /** 319 | * This is a more realistic example with separation of received data creation (mocking) and its processing 320 | */ 321 | bool example3() 322 | { 323 | #pragma pack(push, 1) 324 | struct Sample 325 | { 326 | uint16_t i; 327 | uint16_t q; 328 | }; 329 | struct Header 330 | { 331 | uint16_t frameIndex; 332 | uint8_t blockIndex; 333 | uint8_t filler; 334 | }; 335 | 336 | static const int samplesPerBlock = (512 - sizeof(Header)) / sizeof(Sample); 337 | 338 | struct ProtectedBlock 339 | { 340 | Sample samples[samplesPerBlock]; 341 | }; 342 | struct SuperBlock 343 | { 344 | Header header; 345 | ProtectedBlock protectedBlock; 346 | }; 347 | #pragma pack(pop) 348 | 349 | CM256 cm256; 350 | 351 | if (!cm256.isInitialized()) 352 | { 353 | return false; 354 | } 355 | 356 | CM256::cm256_encoder_params params; 357 | 358 | // Number of bytes per file block 359 | params.BlockBytes = sizeof(ProtectedBlock); 360 | 361 | // Number of blocks 362 | params.OriginalCount = 128; // Superframe = set of protected frames 363 | 364 | // Number of additional recovery blocks generated by encoder 365 | params.RecoveryCount = 32; 366 | 367 | SuperBlock* txBuffer = new SuperBlock[params.OriginalCount+params.RecoveryCount]; 368 | ProtectedBlock* txRecovery = new ProtectedBlock[params.RecoveryCount]; 369 | CM256::cm256_block txDescriptorBlocks[params.OriginalCount+params.RecoveryCount]; 370 | int frameCount = 0; 371 | 372 | // Fill original data 373 | for (int i = 0; i < params.OriginalCount+params.RecoveryCount; ++i) 374 | { 375 | txBuffer[i].header.frameIndex = frameCount; 376 | txBuffer[i].header.blockIndex = i; 377 | 378 | if (i < params.OriginalCount) 379 | { 380 | txBuffer[i].protectedBlock.samples[0].i = i; // marker 381 | } 382 | else 383 | { 384 | memset((void *) &txBuffer[i].protectedBlock, 0, sizeof(ProtectedBlock)); 385 | } 386 | 387 | txDescriptorBlocks[i].Block = (void *) &txBuffer[i].protectedBlock; 388 | txDescriptorBlocks[i].Index = txBuffer[i].header.blockIndex; 389 | } 390 | 391 | // Generate recovery data 392 | 393 | long long ts = getUSecs(); 394 | 395 | if (cm256.cm256_encode(params, txDescriptorBlocks, txRecovery)) 396 | { 397 | std::cerr << "example2: encode failed" << std::endl; 398 | delete[] txBuffer; 399 | delete[] txRecovery; 400 | return false; 401 | } 402 | 403 | long long usecs = getUSecs() - ts; 404 | 405 | std::cerr << "Encoded in " << usecs << " microseconds" << std::endl; 406 | 407 | // insert recovery data in sent data 408 | for (int i = 0; i < params.RecoveryCount; i++) 409 | { 410 | txBuffer[params.OriginalCount+i].protectedBlock = txRecovery[i]; 411 | } 412 | 413 | SuperBlock* rxBuffer = new SuperBlock[params.OriginalCount + params.RecoveryCount]; // received blocks 414 | int k = 0; 415 | 416 | for (int i = 0; i < params.OriginalCount+params.RecoveryCount; i++) 417 | { 418 | if (i % 5 != 4) 419 | { 420 | rxBuffer[k] = txBuffer[i]; 421 | k++; 422 | } 423 | } 424 | 425 | Sample *samplesBuffer = new Sample[samplesPerBlock * params.OriginalCount]; 426 | ProtectedBlock* retrievedDataBuffer = (ProtectedBlock *) samplesBuffer; 427 | ProtectedBlock* recoveryBuffer = new ProtectedBlock[params.OriginalCount]; // recovery blocks with maximum size 428 | 429 | CM256::cm256_block rxDescriptorBlocks[params.OriginalCount]; 430 | int recoveryStartIndex; 431 | k = 0; 432 | 433 | for (int i = 0; i < params.OriginalCount; i++) 434 | { 435 | int blockIndex = rxBuffer[i].header.blockIndex; 436 | 437 | if (blockIndex < params.OriginalCount) // it's an original block 438 | { 439 | retrievedDataBuffer[blockIndex] = rxBuffer[i].protectedBlock; 440 | rxDescriptorBlocks[i].Block = (void *) &retrievedDataBuffer[blockIndex]; 441 | rxDescriptorBlocks[i].Index = blockIndex; 442 | } 443 | else // it's a recovery block 444 | { 445 | if (k == 0) 446 | { 447 | recoveryStartIndex = i; 448 | } 449 | 450 | recoveryBuffer[k] = rxBuffer[i].protectedBlock; 451 | rxDescriptorBlocks[i].Block = (void *) &recoveryBuffer[k]; 452 | rxDescriptorBlocks[i].Index = blockIndex; 453 | k++; 454 | } 455 | } 456 | 457 | ts = getUSecs(); 458 | 459 | if (cm256.cm256_decode(params, rxDescriptorBlocks)) 460 | { 461 | delete[] txBuffer; 462 | delete[] txRecovery; 463 | delete[] rxBuffer; 464 | delete[] samplesBuffer; 465 | delete[] recoveryBuffer; 466 | 467 | return false; 468 | } 469 | 470 | usecs = getUSecs() - ts; 471 | 472 | for (int i = 0; i < k; i++) // recover missing blocks 473 | { 474 | int blockIndex = rxDescriptorBlocks[recoveryStartIndex+i].Index; 475 | retrievedDataBuffer[blockIndex] = recoveryBuffer[i]; 476 | } 477 | 478 | for (int i = 0; i < params.OriginalCount; i++) 479 | { 480 | std::cerr << i << ":" 481 | << (unsigned int) rxDescriptorBlocks[i].Index << ":" 482 | << (unsigned int) retrievedDataBuffer[i].samples[0].i << std::endl; 483 | } 484 | 485 | std::cerr << "Decoded in " << usecs << " microseconds" << std::endl; 486 | 487 | delete[] txBuffer; 488 | delete[] txRecovery; 489 | delete[] rxBuffer; 490 | delete[] samplesBuffer; 491 | delete[] recoveryBuffer; 492 | 493 | return true; 494 | } 495 | 496 | /** 497 | * This is a more realistic example with separation of received data creation (mocking) and its processing 498 | */ 499 | bool example4() 500 | { 501 | #pragma pack(push, 1) 502 | struct Sample 503 | { 504 | uint16_t i; 505 | uint16_t q; 506 | }; 507 | struct Header 508 | { 509 | uint16_t frameIndex; 510 | uint8_t blockIndex; 511 | uint8_t filler; 512 | }; 513 | 514 | static const int samplesPerBlock = (512 - sizeof(Header)) / sizeof(Sample); 515 | 516 | struct ProtectedBlock 517 | { 518 | Sample samples[samplesPerBlock]; 519 | }; 520 | struct SuperBlock 521 | { 522 | Header header; 523 | ProtectedBlock protectedBlock; 524 | }; 525 | #pragma pack(pop) 526 | 527 | CM256 cm256; 528 | if (!cm256.isInitialized()) 529 | { 530 | return false; 531 | } 532 | 533 | CM256::cm256_encoder_params params; 534 | 535 | // Number of bytes per file block 536 | params.BlockBytes = sizeof(ProtectedBlock); 537 | 538 | // Number of blocks 539 | params.OriginalCount = 128; // Superframe = set of protected frames 540 | 541 | // Number of additional recovery blocks generated by encoder 542 | params.RecoveryCount = 25; 543 | 544 | SuperBlock txBuffer[256]; 545 | ProtectedBlock txRecovery[256]; 546 | CM256::cm256_block txDescriptorBlocks[256]; 547 | int frameCount = 0; 548 | 549 | // Fill original data 550 | for (int i = 0; i < params.OriginalCount+params.RecoveryCount; ++i) 551 | { 552 | txBuffer[i].header.frameIndex = frameCount; 553 | txBuffer[i].header.blockIndex = i; 554 | txDescriptorBlocks[i].Block = (void *) &txBuffer[i].protectedBlock; 555 | txDescriptorBlocks[i].Index = txBuffer[i].header.blockIndex; 556 | 557 | if (i < params.OriginalCount) 558 | { 559 | for (int k = 0; k < samplesPerBlock; k++) 560 | { 561 | txBuffer[i].protectedBlock.samples[k].i = rand(); 562 | txBuffer[i].protectedBlock.samples[k].q = rand(); 563 | } 564 | } 565 | else 566 | { 567 | memset((void *) &txBuffer[i].protectedBlock, 0, sizeof(ProtectedBlock)); 568 | } 569 | 570 | } 571 | 572 | // Generate recovery data 573 | 574 | long long ts = getUSecs(); 575 | 576 | if (cm256.cm256_encode(params, txDescriptorBlocks, txRecovery)) 577 | { 578 | std::cerr << "example2: encode failed" << std::endl; 579 | return false; 580 | } 581 | 582 | long long usecs = getUSecs() - ts; 583 | 584 | std::cerr << "Encoded in " << usecs << " microseconds" << std::endl; 585 | 586 | // insert recovery data in sent data 587 | for (int i = 0; i < params.RecoveryCount; i++) 588 | { 589 | txBuffer[params.OriginalCount+i].protectedBlock = txRecovery[i]; 590 | } 591 | 592 | SuperBlock* rxBuffer = new SuperBlock[256]; // received blocks 593 | int nbRxBlocks = 0; 594 | 595 | for (int i = 0; i < params.OriginalCount+params.RecoveryCount; i++) 596 | { 597 | if (i % 6 != 4) 598 | //if (i != 101) 599 | { 600 | rxBuffer[nbRxBlocks] = txBuffer[i]; 601 | nbRxBlocks++; 602 | } 603 | } 604 | 605 | std::cerr << "exemple4: nbRxBlocks: " << nbRxBlocks << " OriginalCount: " << params.OriginalCount << std::endl; 606 | 607 | Sample *samplesBuffer = new Sample[samplesPerBlock * (params.OriginalCount - 1)]; 608 | ProtectedBlock blockZero; 609 | ProtectedBlock* retrievedDataBuffer = (ProtectedBlock *) samplesBuffer; 610 | ProtectedBlock* recoveryBuffer = new ProtectedBlock[params.OriginalCount]; // recovery blocks with maximum size 611 | 612 | CM256::cm256_block rxDescriptorBlocks[params.OriginalCount]; 613 | int recoveryStartIndex = 0; 614 | int recoveryCount = 0; 615 | int nbBlocks = 0; 616 | 617 | for (int i = 0; i < nbRxBlocks; i++) 618 | { 619 | int blockIndex = rxBuffer[i].header.blockIndex; 620 | 621 | if (nbBlocks < params.OriginalCount) // not enough data store it 622 | { 623 | rxDescriptorBlocks[i].Index = blockIndex; 624 | 625 | if (blockIndex == 0) // it is block #0 626 | { 627 | blockZero = rxBuffer[i].protectedBlock; 628 | rxDescriptorBlocks[i].Block = (void *) &blockZero; 629 | } 630 | else if (blockIndex < params.OriginalCount) // it's an original block 631 | { 632 | retrievedDataBuffer[blockIndex - 1] = rxBuffer[i].protectedBlock; 633 | rxDescriptorBlocks[i].Block = (void *) &retrievedDataBuffer[blockIndex - 1]; 634 | } 635 | else // it's a recovery block 636 | { 637 | if (recoveryCount == 0) 638 | { 639 | recoveryStartIndex = i; 640 | } 641 | 642 | recoveryBuffer[recoveryCount] = rxBuffer[i].protectedBlock; 643 | rxDescriptorBlocks[i].Block = (void *) &recoveryBuffer[recoveryCount]; 644 | recoveryCount++; 645 | } 646 | } 647 | 648 | nbBlocks++; 649 | 650 | if (nbBlocks == params.OriginalCount) // ready 651 | { 652 | if (recoveryCount > 0) 653 | { 654 | ts = getUSecs(); 655 | 656 | if (cm256.cm256_decode(params, rxDescriptorBlocks)) 657 | { 658 | delete[] rxBuffer; 659 | delete[] samplesBuffer; 660 | delete[] recoveryBuffer; 661 | 662 | return false; 663 | } 664 | 665 | usecs = getUSecs() - ts; 666 | std::cerr << "recover missing blocks..." << std::endl; 667 | 668 | for (int ir = 0; ir < recoveryCount; ir++) // recover missing blocks 669 | { 670 | int blockIndex = rxDescriptorBlocks[recoveryStartIndex+ir].Index; 671 | 672 | if (blockIndex == 0) // it is block #0 673 | { 674 | blockZero = recoveryBuffer[ir]; 675 | } 676 | else 677 | { 678 | retrievedDataBuffer[blockIndex - 1] = recoveryBuffer[ir]; 679 | } 680 | 681 | std::cerr << ir << ":" << blockIndex << ": " << recoveryBuffer[ir].samples[0].i << std::endl; 682 | } 683 | } 684 | } 685 | } 686 | 687 | std::cerr << "final..." << std::endl; 688 | 689 | for (int i = 1; i < params.OriginalCount; i++) 690 | { 691 | bool compOKi = true; 692 | bool compOKq = true; 693 | 694 | for (int k = 0; k < samplesPerBlock; k++) 695 | { 696 | if (retrievedDataBuffer[i - 1].samples[k].i != txBuffer[i].protectedBlock.samples[k].i) 697 | { 698 | std::cerr << i << ": error: " << k << ": i: " << retrievedDataBuffer[i].samples[k].i << "/" << txBuffer[i].protectedBlock.samples[k].i << std::endl; 699 | compOKi = false; 700 | break; 701 | } 702 | 703 | if (retrievedDataBuffer[i - 1].samples[k].q != txBuffer[i].protectedBlock.samples[k].q) 704 | { 705 | std::cerr << i << ": error: " << k << ": q: " << retrievedDataBuffer[i].samples[k].q << "/" << txBuffer[i].protectedBlock.samples[k].q << std::endl; 706 | compOKq = false; 707 | break; 708 | } 709 | } 710 | 711 | if (compOKi && compOKq) 712 | { 713 | std::cerr << i << ": OK" << std::endl; 714 | } 715 | } 716 | 717 | // Zero: 718 | 719 | bool compOKi = true; 720 | bool compOKq = true; 721 | 722 | for (int k = 0; k < samplesPerBlock; k++) 723 | { 724 | if (blockZero.samples[k].i != txBuffer[0].protectedBlock.samples[k].i) 725 | { 726 | std::cerr << "zero: error: " << k << ": i: " << blockZero.samples[k].i << "/" << txBuffer[0].protectedBlock.samples[k].i << std::endl; 727 | compOKi = false; 728 | break; 729 | } 730 | 731 | if (blockZero.samples[k].q != txBuffer[0].protectedBlock.samples[k].q) 732 | { 733 | std::cerr << "zero: error: " << k << ": q: " << blockZero.samples[k].q << "/" << txBuffer[0].protectedBlock.samples[k].q << std::endl; 734 | compOKq = false; 735 | break; 736 | } 737 | } 738 | 739 | if (compOKi && compOKq) 740 | { 741 | std::cerr << "zero: OK" << std::endl; 742 | } 743 | 744 | std::cerr << "Decoded in " << usecs << " microseconds" << std::endl; 745 | 746 | delete[] samplesBuffer; 747 | delete[] recoveryBuffer; 748 | 749 | return true; 750 | } // example4 751 | 752 | int main() 753 | { 754 | std::cerr << "ExampleFileUsage:" << std::endl; 755 | 756 | if (!ExampleFileUsage()) 757 | { 758 | std::cerr << "ExampleFileUsage failed" << std::endl << std::endl; 759 | return 1; 760 | } 761 | 762 | std::cerr << "ExampleFileUsage successful" << std::endl << std::endl; 763 | std::cerr << "example2:" << std::endl; 764 | 765 | 766 | if (!example2()) 767 | { 768 | std::cerr << "example2 failed" << std::endl << std::endl; 769 | return 1; 770 | } 771 | 772 | std::cerr << "example2 successful" << std::endl << std::endl; 773 | std::cerr << "example3:" << std::endl; 774 | 775 | if (!example3()) 776 | { 777 | std::cerr << "example3 failed" << std::endl << std::endl; 778 | return 1; 779 | } 780 | 781 | std::cerr << "example3 successful" << std::endl << std::endl; 782 | std::cerr << "example4:" << std::endl; 783 | 784 | if (!example4()) 785 | { 786 | std::cerr << "example4 failed" << std::endl << std::endl; 787 | return 1; 788 | } 789 | 790 | std::cerr << "example4 successful" << std::endl; 791 | 792 | return 0; 793 | } 794 | -------------------------------------------------------------------------------- /unit_test/data.h: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright (c) 2016 Edouard M. Griffiths. All rights reserved. 3 | 4 | Redistribution and use in source and binary forms, with or without 5 | modification, are permitted provided that the following conditions are met: 6 | 7 | * Redistributions of source code must retain the above copyright notice, 8 | this list of conditions and the following disclaimer. 9 | * Redistributions in binary form must reproduce the above copyright notice, 10 | this list of conditions and the following disclaimer in the documentation 11 | and/or other materials provided with the distribution. 12 | * Neither the name of CM256 nor the names of its contributors may be 13 | used to endorse or promote products derived from this software without 14 | specific prior written permission. 15 | 16 | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" 17 | AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 18 | IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 19 | ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE 20 | LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR 21 | CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF 22 | SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS 23 | INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN 24 | CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) 25 | ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE 26 | POSSIBILITY OF SUCH DAMAGE. 27 | */ 28 | 29 | #ifndef UNIT_TEST_DATA_H_ 30 | #define UNIT_TEST_DATA_H_ 31 | 32 | #include 33 | #include 34 | 35 | #pragma pack(push, 1) 36 | struct Sample 37 | { 38 | uint16_t i; 39 | uint16_t q; 40 | }; 41 | struct Header 42 | { 43 | uint16_t frameIndex; 44 | uint8_t blockIndex; 45 | uint8_t filler; 46 | }; 47 | 48 | static const int udpSize = 512; 49 | static const int nbSamplesPerBlock = (512 - sizeof(Header)) / sizeof(Sample); 50 | static const int nbOriginalBlocks = 128; 51 | static const int nbRecoveryBlocks = 26; 52 | 53 | struct ProtectedBlock 54 | { 55 | Sample samples[nbSamplesPerBlock]; 56 | }; 57 | struct SuperBlock 58 | { 59 | Header header; 60 | ProtectedBlock protectedBlock; 61 | }; 62 | 63 | struct MetaDataFEC 64 | { 65 | uint32_t m_centerFrequency; //!< 4 center frequency in kHz 66 | uint32_t m_sampleRate; //!< 8 sample rate in Hz 67 | uint8_t m_sampleBytes; //!< 9 MSB(4): indicators, LSB(4) number of bytes per sample 68 | uint8_t m_sampleBits; //!< 10 number of effective bits per sample 69 | uint8_t m_nbOriginalBlocks; //!< 11 number of blocks with original (protected) data 70 | uint8_t m_nbFECBlocks; //!< 12 number of blocks carrying FEC 71 | uint32_t m_tv_sec; //!< 16 seconds of timestamp at start time of super-frame processing 72 | uint32_t m_tv_usec; //!< 20 microseconds of timestamp at start time of super-frame processing 73 | 74 | bool operator==(const MetaDataFEC& rhs) 75 | { 76 | return (memcmp((const void *) this, (const void *) &rhs, 12) == 0); // Only the 12 first bytes are relevant 77 | } 78 | 79 | void init() 80 | { 81 | memset((void *) this, 0, sizeof(MetaDataFEC)); 82 | } 83 | }; 84 | #pragma pack(pop) 85 | 86 | 87 | #endif /* UNIT_TEST_DATA_H_ */ 88 | -------------------------------------------------------------------------------- /unit_test/example0.cpp: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright (c) 2016 Edouard M. Griffiths. All rights reserved. 3 | 4 | Redistribution and use in source and binary forms, with or without 5 | modification, are permitted provided that the following conditions are met: 6 | 7 | * Redistributions of source code must retain the above copyright notice, 8 | this list of conditions and the following disclaimer. 9 | * Redistributions in binary form must reproduce the above copyright notice, 10 | this list of conditions and the following disclaimer in the documentation 11 | and/or other materials provided with the distribution. 12 | * Neither the name of CM256 nor the names of its contributors may be 13 | used to endorse or promote products derived from this software without 14 | specific prior written permission. 15 | 16 | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" 17 | AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 18 | IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 19 | ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE 20 | LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR 21 | CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF 22 | SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS 23 | INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN 24 | CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) 25 | ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE 26 | POSSIBILITY OF SUCH DAMAGE. 27 | */ 28 | 29 | #include 30 | #include 31 | 32 | #include "mainutils.h" 33 | #include "data.h" 34 | #include "../cm256.h" 35 | 36 | bool example0_rx(const std::string& filename, const std::string& refFilename) 37 | { 38 | #pragma pack(push, 1) 39 | struct FileHeader 40 | { 41 | CM256::cm256_encoder_params m_cm256Params; 42 | int m_txBlocks; 43 | }; 44 | #pragma pack(pop) 45 | 46 | CM256 cm256; 47 | 48 | std::ifstream rxFile; 49 | rxFile.open(filename.c_str(), std::ios::in | std::ios::binary); 50 | 51 | FileHeader fileHeader; 52 | 53 | rxFile.read((char *) &fileHeader, sizeof(FileHeader)); 54 | 55 | std::cerr << "example0 Rx:" 56 | << " BlockBytes: " << fileHeader.m_cm256Params.BlockBytes 57 | << " OriginalCount: " << fileHeader.m_cm256Params.OriginalCount 58 | << " RecoveryCount: " << fileHeader.m_cm256Params.RecoveryCount 59 | << " m_txBlocks: " << fileHeader.m_txBlocks << std::endl; 60 | 61 | SuperBlock* rxBuffer = new SuperBlock[256]; // received blocks 62 | int nbRxBlocks = fileHeader.m_txBlocks; 63 | 64 | for (int i = 0; i < nbRxBlocks; i++) 65 | { 66 | rxFile.read((char *) &rxBuffer[i], sizeof(SuperBlock)); 67 | } 68 | 69 | rxFile.close(); 70 | 71 | Sample *samplesBuffer = new Sample[nbSamplesPerBlock * (fileHeader.m_cm256Params.OriginalCount)]; 72 | ProtectedBlock* retrievedDataBuffer = (ProtectedBlock *) samplesBuffer; 73 | ProtectedBlock* recoveryBuffer = new ProtectedBlock[fileHeader.m_cm256Params.OriginalCount]; 74 | CM256::cm256_block rxDescriptorBlocks[fileHeader.m_cm256Params.OriginalCount]; 75 | int recoveryCount = 0; 76 | int nbBlocks = 0; 77 | 78 | for (int i = 0; i < nbRxBlocks; i++) 79 | { 80 | int blockIndex = rxBuffer[i].header.blockIndex; 81 | 82 | if (nbBlocks < fileHeader.m_cm256Params.OriginalCount) // not enough data store it 83 | { 84 | rxDescriptorBlocks[i].Index = blockIndex; 85 | 86 | if (blockIndex < fileHeader.m_cm256Params.OriginalCount) // it's a data block 87 | { 88 | retrievedDataBuffer[blockIndex] = rxBuffer[i].protectedBlock; 89 | rxDescriptorBlocks[i].Block = (void *) &retrievedDataBuffer[blockIndex]; 90 | } 91 | else // it's a recovery block 92 | { 93 | recoveryBuffer[recoveryCount] = rxBuffer[i].protectedBlock; 94 | rxDescriptorBlocks[i].Block = (void *) &recoveryBuffer[recoveryCount]; 95 | recoveryCount++; 96 | } 97 | } 98 | 99 | nbBlocks++; 100 | 101 | if (nbBlocks == fileHeader.m_cm256Params.OriginalCount) // ready 102 | { 103 | if (recoveryCount > 0) 104 | { 105 | long long ts = getUSecs(); 106 | 107 | if (cm256.cm256_decode(fileHeader.m_cm256Params, rxDescriptorBlocks)) 108 | { 109 | delete[] rxBuffer; 110 | delete[] samplesBuffer; 111 | delete[] recoveryBuffer; 112 | 113 | return false; 114 | } 115 | 116 | long long usecs = getUSecs() - ts; 117 | std::cerr << "recover missing blocks..." << std::endl; 118 | 119 | for (int ir = 0; ir < recoveryCount; ir++) // recover missing blocks 120 | { 121 | int blockIndex = rxDescriptorBlocks[fileHeader.m_cm256Params.OriginalCount - recoveryCount + ir].Index; 122 | retrievedDataBuffer[blockIndex] = recoveryBuffer[ir]; 123 | std::cerr << ir << ":" << blockIndex << ": " << recoveryBuffer[ir].samples[0].i << std::endl; 124 | } 125 | 126 | std::cerr << "Decoded in " << usecs << " microseconds" << std::endl; 127 | 128 | } 129 | } 130 | } 131 | 132 | std::cerr << "final..." << std::endl; 133 | 134 | SuperBlock* refBuffer = new SuperBlock[256]; // reference blocks 135 | std::ifstream refFile; 136 | refFile.open(refFilename.c_str(), std::ios::in | std::ios::binary); 137 | 138 | FileHeader refFileHeader; 139 | 140 | refFile.read((char *) &refFileHeader, sizeof(FileHeader)); 141 | 142 | for (int i = 0; i < refFileHeader.m_cm256Params.OriginalCount + refFileHeader.m_cm256Params.RecoveryCount; i++) 143 | { 144 | refFile.read((char *) &refBuffer[i], sizeof(SuperBlock)); 145 | } 146 | 147 | refFile.close(); 148 | 149 | for (int i = 0; i < fileHeader.m_cm256Params.OriginalCount; i++) 150 | { 151 | bool compOKi = true; 152 | bool compOKq = true; 153 | 154 | for (int k = 0; k < nbSamplesPerBlock; k++) 155 | { 156 | if (retrievedDataBuffer[i].samples[k].i != refBuffer[i].protectedBlock.samples[k].i) 157 | { 158 | std::cerr << i << ": error: " << k << ": i: " << retrievedDataBuffer[i].samples[k].i << "/" << refBuffer[i].protectedBlock.samples[k].i << std::endl; 159 | compOKi = false; 160 | break; 161 | } 162 | 163 | if (retrievedDataBuffer[i].samples[k].q != refBuffer[i].protectedBlock.samples[k].q) 164 | { 165 | std::cerr << i << ": error: " << k << ": q: " << retrievedDataBuffer[i].samples[k].q << "/" << refBuffer[i].protectedBlock.samples[k].q << std::endl; 166 | compOKq = false; 167 | break; 168 | } 169 | } 170 | 171 | if (compOKi && compOKq) 172 | { 173 | std::cerr << i << ": OK" << std::endl; 174 | } 175 | } 176 | 177 | delete[] refBuffer; 178 | delete[] samplesBuffer; 179 | delete[] recoveryBuffer; 180 | delete[] rxBuffer; 181 | 182 | return true; 183 | } 184 | 185 | 186 | bool example0_tx(const std::string& filename, const std::string& refFilename) 187 | { 188 | #pragma pack(push, 1) 189 | struct Sample 190 | { 191 | uint16_t i; 192 | uint16_t q; 193 | }; 194 | struct Header 195 | { 196 | uint16_t frameIndex; 197 | uint8_t blockIndex; 198 | uint8_t filler; 199 | }; 200 | 201 | static const int samplesPerBlock = (512 - sizeof(Header)) / sizeof(Sample); 202 | 203 | struct ProtectedBlock 204 | { 205 | Sample samples[samplesPerBlock]; 206 | }; 207 | struct SuperBlock 208 | { 209 | Header header; 210 | ProtectedBlock protectedBlock; 211 | }; 212 | 213 | struct FileHeader 214 | { 215 | CM256::cm256_encoder_params m_cm256Params; 216 | int m_txBlocks; 217 | }; 218 | #pragma pack(pop) 219 | 220 | CM256 cm256; 221 | 222 | CM256::cm256_encoder_params params; 223 | 224 | // Number of bytes per file block 225 | params.BlockBytes = sizeof(ProtectedBlock); 226 | 227 | // Number of blocks 228 | params.OriginalCount = 128; // Superframe = set of protected frames 229 | 230 | // Number of additional recovery blocks generated by encoder 231 | params.RecoveryCount = 25; 232 | 233 | SuperBlock txBuffer[256]; 234 | ProtectedBlock txRecovery[256]; 235 | CM256::cm256_block txDescriptorBlocks[256]; 236 | int frameCount = 0; 237 | 238 | // Fill original data 239 | for (int i = 0; i < params.OriginalCount+params.RecoveryCount; ++i) 240 | { 241 | txBuffer[i].header.frameIndex = frameCount; 242 | txBuffer[i].header.blockIndex = i; 243 | txDescriptorBlocks[i].Block = (void *) &txBuffer[i].protectedBlock; 244 | txDescriptorBlocks[i].Index = txBuffer[i].header.blockIndex; 245 | 246 | if (i < params.OriginalCount) 247 | { 248 | for (int k = 0; k < samplesPerBlock; k++) 249 | { 250 | txBuffer[i].protectedBlock.samples[k].i = std::rand(); 251 | txBuffer[i].protectedBlock.samples[k].q = std::rand(); 252 | } 253 | } 254 | else 255 | { 256 | memset((void *) &txBuffer[i].protectedBlock, 0, sizeof(ProtectedBlock)); 257 | } 258 | 259 | } 260 | 261 | // Generate recovery data 262 | 263 | long long ts = getUSecs(); 264 | 265 | if (cm256.cm256_encode(params, txDescriptorBlocks, txRecovery)) 266 | { 267 | std::cerr << "example2: encode failed" << std::endl; 268 | return false; 269 | } 270 | 271 | long long usecs = getUSecs() - ts; 272 | 273 | std::cerr << "Encoded in " << usecs << " microseconds" << std::endl; 274 | 275 | // insert recovery data in sent data 276 | for (int i = 0; i < params.RecoveryCount; i++) 277 | { 278 | txBuffer[params.OriginalCount+i].protectedBlock = txRecovery[i]; 279 | } 280 | 281 | // puncture blocks to simulate data loss 282 | 283 | SuperBlock* txFileBuffer = new SuperBlock[256]; // received blocks 284 | int nbTxFileBlocks = 0; 285 | 286 | for (int i = 0; i < params.OriginalCount+params.RecoveryCount; i++) 287 | { 288 | if (i % 6 != 4) 289 | //if (i != 101) 290 | { 291 | txFileBuffer[nbTxFileBlocks] = txBuffer[i]; 292 | nbTxFileBlocks++; 293 | } 294 | } 295 | 296 | FileHeader fileHeader; 297 | fileHeader.m_cm256Params = params; 298 | fileHeader.m_txBlocks = nbTxFileBlocks; 299 | 300 | std::ofstream txFile; 301 | txFile.open(filename.c_str(), std::ios::out | std::ios::binary); 302 | 303 | txFile.write((const char *) &fileHeader, sizeof(FileHeader)); 304 | 305 | for (int i = 0; i < nbTxFileBlocks; i++) 306 | { 307 | txFile.write((const char *) &txFileBuffer[i], sizeof(SuperBlock)); 308 | } 309 | 310 | txFile.close(); 311 | 312 | // reference 313 | 314 | std::ofstream refFile; 315 | refFile.open(refFilename.c_str(), std::ios::out | std::ios::binary); 316 | 317 | refFile.write((const char *) &fileHeader, sizeof(FileHeader)); 318 | 319 | for (int i = 0; i < params.OriginalCount+params.RecoveryCount; i++) 320 | { 321 | refFile.write((const char *) &txBuffer[i], sizeof(SuperBlock)); 322 | } 323 | 324 | refFile.close(); 325 | 326 | return true; 327 | } 328 | 329 | -------------------------------------------------------------------------------- /unit_test/example0.h: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright (c) 2016 Edouard M. Griffiths. All rights reserved. 3 | 4 | Redistribution and use in source and binary forms, with or without 5 | modification, are permitted provided that the following conditions are met: 6 | 7 | * Redistributions of source code must retain the above copyright notice, 8 | this list of conditions and the following disclaimer. 9 | * Redistributions in binary form must reproduce the above copyright notice, 10 | this list of conditions and the following disclaimer in the documentation 11 | and/or other materials provided with the distribution. 12 | * Neither the name of CM256 nor the names of its contributors may be 13 | used to endorse or promote products derived from this software without 14 | specific prior written permission. 15 | 16 | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" 17 | AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 18 | IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 19 | ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE 20 | LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR 21 | CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF 22 | SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS 23 | INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN 24 | CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) 25 | ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE 26 | POSSIBILITY OF SUCH DAMAGE. 27 | */ 28 | 29 | #ifndef UNIT_TEST_EXAMPLE0_H_ 30 | #define UNIT_TEST_EXAMPLE0_H_ 31 | 32 | #include 33 | 34 | bool example0_tx(const std::string& filename, const std::string& refFilename); 35 | bool example0_rx(const std::string& filename, const std::string& refFilename); 36 | 37 | #endif /* UNIT_TEST_EXAMPLE0_H_ */ 38 | -------------------------------------------------------------------------------- /unit_test/example1.cpp: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright (c) 2016 Edouard M. Griffiths. All rights reserved. 3 | 4 | Redistribution and use in source and binary forms, with or without 5 | modification, are permitted provided that the following conditions are met: 6 | 7 | * Redistributions of source code must retain the above copyright notice, 8 | this list of conditions and the following disclaimer. 9 | * Redistributions in binary form must reproduce the above copyright notice, 10 | this list of conditions and the following disclaimer in the documentation 11 | and/or other materials provided with the distribution. 12 | * Neither the name of CM256 nor the names of its contributors may be 13 | used to endorse or promote products derived from this software without 14 | specific prior written permission. 15 | 16 | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" 17 | AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 18 | IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 19 | ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE 20 | LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR 21 | CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF 22 | SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS 23 | INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN 24 | CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) 25 | ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE 26 | POSSIBILITY OF SUCH DAMAGE. 27 | */ 28 | 29 | #include 30 | #include 31 | #include 32 | #include 33 | #include "example1.h" 34 | 35 | 36 | Example1Tx::Example1Tx(int samplesPerBlock, int nbOriginalBlocks, int nbFecBlocks) 37 | { 38 | m_params.BlockBytes = samplesPerBlock * sizeof(Sample); 39 | m_params.OriginalCount = nbOriginalBlocks; 40 | m_params.RecoveryCount = nbFecBlocks; 41 | m_cm256_OK = m_cm256.isInitialized(); 42 | } 43 | 44 | Example1Tx::~Example1Tx() 45 | { 46 | } 47 | 48 | void Example1Tx::makeDataBlocks(SuperBlock *txBlocks, uint16_t frameNumber) 49 | { 50 | std::srand(frameNumber); 51 | 52 | for (int iblock = 0; iblock < m_params.OriginalCount; iblock++) 53 | { 54 | txBlocks[iblock].header.frameIndex = frameNumber; 55 | txBlocks[iblock].header.blockIndex = (uint8_t) iblock; 56 | 57 | if (iblock == 0) // meta data 58 | { 59 | MetaDataFEC *metaData = (MetaDataFEC *) &txBlocks[iblock].protectedBlock; 60 | metaData->init(); 61 | metaData->m_nbOriginalBlocks = m_params.OriginalCount; 62 | metaData->m_nbFECBlocks = m_params.RecoveryCount; 63 | struct timeval tv; 64 | gettimeofday(&tv, 0); 65 | metaData->m_tv_sec = tv.tv_sec; 66 | metaData->m_tv_usec = tv.tv_usec; 67 | } 68 | else 69 | { 70 | for (int isample = 0; isample < nbSamplesPerBlock; isample++) 71 | { 72 | txBlocks[iblock].protectedBlock.samples[isample].i = std::rand(); 73 | txBlocks[iblock].protectedBlock.samples[isample].q = std::rand(); 74 | } 75 | } 76 | } 77 | } 78 | 79 | bool Example1Tx::makeFecBlocks(SuperBlock *txBlocks, uint16_t frameIndex) 80 | { 81 | if (m_params.RecoveryCount > 0) 82 | { 83 | for (int i = 0; i < m_params.OriginalCount; i++) 84 | { 85 | m_txDescriptorBlocks[i].Block = (void *) &txBlocks[i].protectedBlock; 86 | m_txDescriptorBlocks[i].Index = i; 87 | } 88 | 89 | if (m_cm256_OK) 90 | { 91 | if (m_cm256.cm256_encode(m_params, m_txDescriptorBlocks, m_txRecovery)) 92 | { 93 | std::cerr << "example2: encode failed" << std::endl; 94 | return false; 95 | } 96 | 97 | for (int i = 0; i < m_params.RecoveryCount; i++) 98 | { 99 | txBlocks[i + m_params.OriginalCount].header.blockIndex = i + m_params.OriginalCount; 100 | txBlocks[i + m_params.OriginalCount].header.frameIndex = frameIndex; 101 | txBlocks[i + m_params.OriginalCount].protectedBlock = m_txRecovery[i]; 102 | } 103 | } 104 | } 105 | 106 | return true; 107 | } 108 | 109 | void Example1Tx::transmitBlocks(SuperBlock *txBlocks, 110 | const std::string& destaddress, 111 | int destport, 112 | std::vector& blockExclusionList, 113 | int txDelay) 114 | { 115 | std::vector::iterator exclusionIt = blockExclusionList.begin(); 116 | 117 | for (int i = 0; i < m_params.OriginalCount + m_params.RecoveryCount; i++) 118 | { 119 | if ((exclusionIt != blockExclusionList.end()) && (*exclusionIt == i)) 120 | { 121 | ++exclusionIt; 122 | continue; 123 | } 124 | 125 | m_socket.SendDataGram((const void *) &txBlocks[i], (int) udpSize, destaddress, destport); 126 | usleep(txDelay); 127 | } 128 | 129 | usleep(100*txDelay); // wait at end of frame to let Rx process it 130 | } 131 | 132 | Example1Rx::Example1Rx(int samplesPerBlock, int nbOriginalBlocks, int nbFecBlocks) : 133 | m_frameHead(0), 134 | m_frameCount(0), 135 | m_blockCount(0), 136 | m_metaReceived(false), 137 | m_dataCount(0), 138 | m_recoveryCount(0) 139 | { 140 | m_params.BlockBytes = samplesPerBlock * sizeof(Sample); 141 | m_params.OriginalCount = nbOriginalBlocks; 142 | m_params.RecoveryCount = nbFecBlocks; 143 | m_currentMeta.init(); 144 | m_cm256_OK = m_cm256.isInitialized(); 145 | } 146 | 147 | Example1Rx::~Example1Rx() 148 | { 149 | } 150 | 151 | void Example1Rx::processBlock(SuperBlock& superBlock) 152 | { 153 | if (superBlock.header.frameIndex != m_frameHead) 154 | { 155 | if (m_dataCount != m_params.OriginalCount) 156 | { 157 | std::cerr << "Example1Rx::processBlock: incomplete frame" << std::endl; 158 | } 159 | 160 | m_frameCount++; 161 | m_blockCount = 0; 162 | m_metaReceived = false; 163 | m_dataCount = 0; 164 | m_recoveryCount = 0; 165 | m_frameHead = superBlock.header.frameIndex; 166 | } 167 | 168 | if (m_blockCount < m_params.OriginalCount) // not enough to decode => store data 169 | { 170 | int blockIndex = superBlock.header.blockIndex; 171 | 172 | if (blockIndex < m_params.OriginalCount) // data 173 | { 174 | m_data[blockIndex] = superBlock.protectedBlock; 175 | m_descriptorBlocks[m_blockCount].Block = (void *) &m_data[blockIndex]; 176 | m_descriptorBlocks[m_blockCount].Index = blockIndex; 177 | m_dataCount++; 178 | 179 | if (blockIndex == 0) 180 | { 181 | MetaDataFEC *metaData = (MetaDataFEC *) &m_data[blockIndex]; 182 | 183 | if (!(*metaData == m_currentMeta)) 184 | { 185 | m_currentMeta = *metaData; 186 | } 187 | 188 | m_metaReceived = true; 189 | } 190 | 191 | // if (blockIndex == 1) 192 | // { 193 | // std::srand(superBlock.header.frameIndex); 194 | // std::cerr << "Example1Rx::processBlock: " << superBlock.header.frameIndex << ": "; 195 | // 196 | // for (int k = 0; k < 2; k++) 197 | // { 198 | // uint16_t refI = std::rand(); 199 | // uint16_t refQ = std::rand(); 200 | // 201 | // std::cerr << "[" << k << "] " << m_data[blockIndex].samples[k].i 202 | // << "/" << m_data[blockIndex].samples[k].q 203 | // << " " << refI 204 | // << "/" << refQ 205 | // << " "; 206 | // } 207 | // 208 | // std::cerr << std::endl; 209 | // } 210 | 211 | } 212 | else // recovery data 213 | { 214 | m_recovery[m_recoveryCount] = superBlock.protectedBlock; 215 | m_descriptorBlocks[m_blockCount].Block = (void *) &m_recovery[m_recoveryCount]; 216 | m_descriptorBlocks[m_blockCount].Index = blockIndex; 217 | m_recoveryCount++; 218 | } 219 | } 220 | 221 | m_blockCount++; 222 | 223 | if (m_blockCount == m_params.OriginalCount) // enough data is received 224 | { 225 | if (m_cm256_OK && (m_recoveryCount > 0)) // FEC necessary 226 | { 227 | if (m_cm256.cm256_decode(m_params, m_descriptorBlocks)) // failure to decode 228 | { 229 | std::cerr << "Example1Rx::processBlock: CM256 decode error" << std::endl; 230 | } 231 | else // success to decode 232 | { 233 | std::cerr << "Example1Rx::processBlock: CM256 decode success: "; 234 | 235 | int recoveryStart = m_dataCount; 236 | 237 | for (int ir = 0; ir < m_recoveryCount; ir++) 238 | { 239 | int blockIndex = m_descriptorBlocks[recoveryStart + ir].Index; 240 | std::cerr << blockIndex << " "; 241 | m_data[blockIndex] = *((ProtectedBlock *) m_descriptorBlocks[recoveryStart + ir].Block); 242 | m_dataCount++; 243 | } 244 | 245 | std::cerr << std::endl; 246 | } 247 | } 248 | 249 | if (m_dataCount == m_params.OriginalCount) 250 | { 251 | checkData(); 252 | } 253 | } 254 | } 255 | 256 | bool Example1Rx::checkData() 257 | { 258 | bool compOKi = true; 259 | bool compOKq = true; 260 | 261 | std::srand(m_frameHead); 262 | 263 | for (int i = 1; i < m_params.OriginalCount; i++) 264 | { 265 | compOKi = true; 266 | compOKq = true; 267 | 268 | for (int k = 0; k < nbSamplesPerBlock; k++) 269 | { 270 | uint16_t refI = std::rand(); 271 | uint16_t refQ = std::rand(); 272 | 273 | if (m_data[i].samples[k].i != refI) 274 | { 275 | std::cerr << i << ": error: " << k << ": i: " << m_data[i].samples[k].i << "/" << refI << std::endl; 276 | compOKi = false; 277 | break; 278 | } 279 | 280 | if (m_data[i].samples[k].q != refQ) 281 | { 282 | std::cerr << i << ": error: " << k << ": q: " << m_data[i].samples[k].q << "/" << refQ << std::endl; 283 | compOKq = false; 284 | break; 285 | } 286 | } 287 | 288 | if (compOKi && compOKq) 289 | { 290 | std::cerr << "."; 291 | } 292 | else 293 | { 294 | break; 295 | } 296 | } 297 | 298 | if (compOKi && compOKq) 299 | { 300 | std::cerr << "OK" << std::endl; 301 | return true; 302 | } 303 | else 304 | { 305 | return false; 306 | } 307 | } 308 | 309 | bool example1_tx(const std::string& dataaddress, int dataport, std::vector &blockExclusionList, std::atomic_bool& stopFlag) 310 | { 311 | SuperBlock txBlocks[256]; 312 | Example1Tx ex1(nbSamplesPerBlock, nbOriginalBlocks, nbRecoveryBlocks); 313 | 314 | std::cerr << "example1_tx: transmitting on address: " << dataaddress << " port: " << dataport << std::endl; 315 | 316 | for (uint16_t frameNumber = 0; !stopFlag.load(); frameNumber++) 317 | { 318 | ex1.makeDataBlocks(txBlocks, frameNumber); 319 | 320 | if (!ex1.makeFecBlocks(txBlocks, frameNumber)) 321 | { 322 | std::cerr << "example1_tx: encode error" << std::endl; 323 | break; 324 | } 325 | 326 | ex1.transmitBlocks(txBlocks, dataaddress, dataport, blockExclusionList, 300); 327 | 328 | std::cerr << "."; 329 | } 330 | 331 | return true; 332 | } 333 | 334 | bool example1_rx(const std::string& dataaddress, unsigned short dataport, std::atomic_bool& stopFlag) 335 | { 336 | SuperBlock rxBlock; 337 | uint8_t rawBlock[sizeof(SuperBlock)]; 338 | uint32_t rawBlockSize; 339 | UDPSocket rxSocket(dataport); 340 | std::string senderaddress, senderaddress0; 341 | unsigned short senderport, senderport0 = 0; 342 | Example1Rx ex1(nbSamplesPerBlock, nbOriginalBlocks, nbRecoveryBlocks); 343 | 344 | std::cerr << "example1_rx: receiving on address: " << dataaddress << " port: " << (int) dataport << std::endl; 345 | 346 | while (!stopFlag.load()) 347 | { 348 | rawBlockSize = 0; 349 | 350 | while (rawBlockSize < sizeof(SuperBlock)) 351 | { 352 | rawBlockSize += rxSocket.RecvDataGram((void *) &rawBlock[rawBlockSize], (int) sizeof(SuperBlock), senderaddress, senderport); 353 | 354 | if ((senderaddress != senderaddress0) || (senderport != senderport0)) 355 | { 356 | std::cerr << "example1_rx: connected to: " << senderaddress << ":" << senderport << std::endl; 357 | senderaddress0 = senderaddress; 358 | senderport0 = senderport; 359 | } 360 | 361 | usleep(10); 362 | } 363 | 364 | memcpy(&rxBlock, rawBlock, sizeof(SuperBlock)); 365 | ex1.processBlock(rxBlock); 366 | } 367 | 368 | return true; 369 | } 370 | -------------------------------------------------------------------------------- /unit_test/example1.h: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright (c) 2016 Edouard M. Griffiths. All rights reserved. 3 | 4 | Redistribution and use in source and binary forms, with or without 5 | modification, are permitted provided that the following conditions are met: 6 | 7 | * Redistributions of source code must retain the above copyright notice, 8 | this list of conditions and the following disclaimer. 9 | * Redistributions in binary form must reproduce the above copyright notice, 10 | this list of conditions and the following disclaimer in the documentation 11 | and/or other materials provided with the distribution. 12 | * Neither the name of CM256 nor the names of its contributors may be 13 | used to endorse or promote products derived from this software without 14 | specific prior written permission. 15 | 16 | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" 17 | AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 18 | IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 19 | ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE 20 | LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR 21 | CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF 22 | SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS 23 | INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN 24 | CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) 25 | ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE 26 | POSSIBILITY OF SUCH DAMAGE. 27 | */ 28 | 29 | #ifndef UNIT_TEST_EXAMPLE1_H_ 30 | #define UNIT_TEST_EXAMPLE1_H_ 31 | 32 | #include 33 | #include 34 | #include 35 | #include "data.h" 36 | #include "../cm256.h" 37 | #include "UDPSocket.h" 38 | 39 | class Example1Tx 40 | { 41 | public: 42 | Example1Tx(int samplesPerBlock, int nbOriginalBlocks, int nbFecBlocks); 43 | ~Example1Tx(); 44 | 45 | void makeDataBlocks(SuperBlock *txBlocks, uint16_t frameNumber); 46 | bool makeFecBlocks(SuperBlock *txBlocks, uint16_t frameInde); 47 | void transmitBlocks(SuperBlock *txBlocks, 48 | const std::string& destaddress, 49 | int destport, 50 | std::vector& blockExclusionList, 51 | int txDelay); 52 | 53 | protected: 54 | CM256 m_cm256; 55 | bool m_cm256_OK; 56 | CM256::cm256_encoder_params m_params; 57 | CM256::cm256_block m_txDescriptorBlocks[256]; 58 | ProtectedBlock m_txRecovery[128]; 59 | UDPSocket m_socket; 60 | }; 61 | 62 | class Example1Rx 63 | { 64 | public: 65 | Example1Rx(int samplesPerBlock, int nbOriginalBlocks, int nbFecBlocks); 66 | ~Example1Rx(); 67 | 68 | void processBlock(SuperBlock& superBlock); 69 | 70 | private: 71 | bool checkData(); 72 | 73 | CM256 m_cm256; 74 | uint16_t m_frameHead; 75 | uint64_t m_frameCount; 76 | int m_blockCount; 77 | bool m_metaReceived; 78 | int m_dataCount; 79 | int m_recoveryCount; 80 | bool m_cm256_OK; 81 | MetaDataFEC m_currentMeta; 82 | CM256::cm256_encoder_params m_params; 83 | CM256::cm256_block m_descriptorBlocks[256]; 84 | ProtectedBlock m_data[128]; 85 | ProtectedBlock m_recovery[128]; 86 | }; 87 | 88 | bool example1_tx(const std::string& dataaddress, int dataport, std::vector &blockExclusionList, std::atomic_bool& stopFlag); 89 | bool example1_rx(const std::string& dataaddress, unsigned short dataport, std::atomic_bool& stopFlag); 90 | 91 | #endif /* UNIT_TEST_EXAMPLE1_H_ */ 92 | -------------------------------------------------------------------------------- /unit_test/mainutils.cpp: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright (c) 2016 Edouard M. Griffiths. All rights reserved. 3 | 4 | Redistribution and use in source and binary forms, with or without 5 | modification, are permitted provided that the following conditions are met: 6 | 7 | * Redistributions of source code must retain the above copyright notice, 8 | this list of conditions and the following disclaimer. 9 | * Redistributions in binary form must reproduce the above copyright notice, 10 | this list of conditions and the following disclaimer in the documentation 11 | and/or other materials provided with the distribution. 12 | * Neither the name of CM256 nor the names of its contributors may be 13 | used to endorse or promote products derived from this software without 14 | specific prior written permission. 15 | 16 | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" 17 | AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 18 | IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 19 | ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE 20 | LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR 21 | CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF 22 | SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS 23 | INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN 24 | CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) 25 | ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE 26 | POSSIBILITY OF SUCH DAMAGE. 27 | */ 28 | 29 | #include 30 | #include 31 | #include 32 | #include 33 | 34 | #include "mainutils.h" 35 | 36 | long long getUSecs() 37 | { 38 | struct timeval tp; 39 | gettimeofday(&tp, 0); 40 | return (long long) tp.tv_sec * 1000000L + tp.tv_usec; 41 | } 42 | 43 | bool parse_int(const char *s, int& v, bool allow_unit) 44 | { 45 | char *endp; 46 | long t = strtol(s, &endp, 10); 47 | if (endp == s) 48 | return false; 49 | if ( allow_unit && *endp == 'k' && 50 | t > INT_MIN / 1000 && t < INT_MAX / 1000 ) { 51 | t *= 1000; 52 | endp++; 53 | } 54 | if (*endp != '\0' || t < INT_MIN || t > INT_MAX) 55 | return false; 56 | v = t; 57 | return true; 58 | } 59 | 60 | void badarg(const char *label) 61 | { 62 | fprintf(stderr, "ERROR: Invalid argument for %s\n", label); 63 | } 64 | 65 | bool getIntList(std::vector& listInt, std::string& listString) 66 | { 67 | bool converted_successfully = true; 68 | std::regex splitter(","); 69 | auto list_begin = std::sregex_iterator(listString.begin(), 70 | listString.end(), 71 | splitter); 72 | auto list_end = std::sregex_iterator(); 73 | 74 | try { 75 | for (std::sregex_iterator i = list_begin; i != list_end; ++i) { 76 | listInt.push_back(std::stoi(i->str())); 77 | } 78 | } catch (std::invalid_argument & exception) { 79 | converted_successfully = false; 80 | fprintf(stderr, "ERROR: Invalid element: %s\n", listString.c_str()); 81 | } catch(std::out_of_range & exception) { 82 | converted_successfully = false; 83 | fprintf(stderr, "ERROR: Element out of range: %s\n", listString.c_str()); 84 | } 85 | 86 | return converted_successfully; 87 | } 88 | -------------------------------------------------------------------------------- /unit_test/mainutils.h: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright (c) 2016 Edouard M. Griffiths. All rights reserved. 3 | 4 | Redistribution and use in source and binary forms, with or without 5 | modification, are permitted provided that the following conditions are met: 6 | 7 | * Redistributions of source code must retain the above copyright notice, 8 | this list of conditions and the following disclaimer. 9 | * Redistributions in binary form must reproduce the above copyright notice, 10 | this list of conditions and the following disclaimer in the documentation 11 | and/or other materials provided with the distribution. 12 | * Neither the name of CM256 nor the names of its contributors may be 13 | used to endorse or promote products derived from this software without 14 | specific prior written permission. 15 | 16 | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" 17 | AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 18 | IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 19 | ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE 20 | LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR 21 | CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF 22 | SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS 23 | INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN 24 | CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) 25 | ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE 26 | POSSIBILITY OF SUCH DAMAGE. 27 | */ 28 | 29 | #ifndef UNIT_TEST_MAINUTILS_H_ 30 | #define UNIT_TEST_MAINUTILS_H_ 31 | 32 | #include 33 | #include 34 | 35 | long long getUSecs(); 36 | bool parse_int(const char *s, int& v, bool allow_unit=false); 37 | void badarg(const char *label); 38 | bool getIntList(std::vector& listInt, std::string& listString); 39 | 40 | #endif /* UNIT_TEST_MAINUTILS_H_ */ 41 | -------------------------------------------------------------------------------- /unit_test/matrix_test.sln: -------------------------------------------------------------------------------- 1 |  2 | Microsoft Visual Studio Solution File, Format Version 12.00 3 | # Visual Studio 2013 4 | VisualStudioVersion = 12.0.40629.0 5 | MinimumVisualStudioVersion = 10.0.40219.1 6 | Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "matrix_test", "matrix_test.vcxproj", "{7685F962-9880-43F5-A10E-B2A1E0E09465}" 7 | EndProject 8 | Global 9 | GlobalSection(SolutionConfigurationPlatforms) = preSolution 10 | Debug|Win32 = Debug|Win32 11 | Debug|x64 = Debug|x64 12 | Release|Win32 = Release|Win32 13 | Release|x64 = Release|x64 14 | EndGlobalSection 15 | GlobalSection(ProjectConfigurationPlatforms) = postSolution 16 | {7685F962-9880-43F5-A10E-B2A1E0E09465}.Debug|Win32.ActiveCfg = Debug|Win32 17 | {7685F962-9880-43F5-A10E-B2A1E0E09465}.Debug|Win32.Build.0 = Debug|Win32 18 | {7685F962-9880-43F5-A10E-B2A1E0E09465}.Debug|x64.ActiveCfg = Debug|x64 19 | {7685F962-9880-43F5-A10E-B2A1E0E09465}.Debug|x64.Build.0 = Debug|x64 20 | {7685F962-9880-43F5-A10E-B2A1E0E09465}.Release|Win32.ActiveCfg = Release|Win32 21 | {7685F962-9880-43F5-A10E-B2A1E0E09465}.Release|Win32.Build.0 = Release|Win32 22 | {7685F962-9880-43F5-A10E-B2A1E0E09465}.Release|x64.ActiveCfg = Release|x64 23 | {7685F962-9880-43F5-A10E-B2A1E0E09465}.Release|x64.Build.0 = Release|x64 24 | EndGlobalSection 25 | GlobalSection(SolutionProperties) = preSolution 26 | HideSolutionNode = FALSE 27 | EndGlobalSection 28 | EndGlobal 29 | -------------------------------------------------------------------------------- /unit_test/matrix_test.vcxproj: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | 5 | Debug 6 | Win32 7 | 8 | 9 | Debug 10 | x64 11 | 12 | 13 | Release 14 | Win32 15 | 16 | 17 | Release 18 | x64 19 | 20 | 21 | 22 | {7685F962-9880-43F5-A10E-B2A1E0E09465} 23 | matrix_test 24 | 25 | 26 | 27 | Application 28 | true 29 | v120 30 | MultiByte 31 | 32 | 33 | Application 34 | true 35 | v120 36 | MultiByte 37 | 38 | 39 | Application 40 | false 41 | v120 42 | true 43 | MultiByte 44 | 45 | 46 | Application 47 | false 48 | v120 49 | true 50 | MultiByte 51 | 52 | 53 | 54 | 55 | 56 | 57 | 58 | 59 | 60 | 61 | 62 | 63 | 64 | 65 | 66 | 67 | 68 | 69 | 70 | 71 | Level4 72 | Disabled 73 | true 74 | true 75 | false 76 | true 77 | MultiThreadedDebug 78 | false 79 | false 80 | 81 | 82 | true 83 | 84 | 85 | 86 | 87 | Level4 88 | Disabled 89 | true 90 | true 91 | false 92 | true 93 | MultiThreadedDebug 94 | false 95 | false 96 | 97 | 98 | true 99 | 100 | 101 | 102 | 103 | Level4 104 | Full 105 | true 106 | true 107 | true 108 | true 109 | AnySuitable 110 | Size 111 | true 112 | false 113 | false 114 | MultiThreaded 115 | false 116 | false 117 | 118 | 119 | true 120 | true 121 | true 122 | 123 | 124 | 125 | 126 | Level4 127 | Full 128 | true 129 | true 130 | true 131 | true 132 | AnySuitable 133 | Size 134 | true 135 | false 136 | false 137 | MultiThreaded 138 | false 139 | false 140 | 141 | 142 | true 143 | true 144 | true 145 | 146 | 147 | 148 | 149 | 150 | 151 | 152 | 153 | 154 | 155 | 156 | 157 | 158 | 159 | -------------------------------------------------------------------------------- /unit_test/matrix_test.vcxproj.filters: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | 5 | {4FC737F1-C7A5-4376-A066-2A32D752A2FF} 6 | cpp;c;cc;cxx;def;odl;idl;hpj;bat;asm;asmx 7 | 8 | 9 | {93995380-89BD-4b04-88EB-625FBE52EBFB} 10 | h;hh;hpp;hxx;hm;inl;inc;xsd 11 | 12 | 13 | {67DA6AB6-F800-4c08-8B7A-83BB121AAD01} 14 | rc;ico;cur;bmp;dlg;rc2;rct;bin;rgs;gif;jpg;jpeg;jpe;resx;tiff;tif;png;wav;mfcribbon-ms 15 | 16 | 17 | 18 | 19 | Source Files 20 | 21 | 22 | Source Files 23 | 24 | 25 | Source Files 26 | 27 | 28 | 29 | 30 | Source Files 31 | 32 | 33 | Source Files 34 | 35 | 36 | -------------------------------------------------------------------------------- /unit_test/receive.cpp: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright (c) 2016 Edouard M. Griffiths. All rights reserved. 3 | 4 | Redistribution and use in source and binary forms, with or without 5 | modification, are permitted provided that the following conditions are met: 6 | 7 | * Redistributions of source code must retain the above copyright notice, 8 | this list of conditions and the following disclaimer. 9 | * Redistributions in binary form must reproduce the above copyright notice, 10 | this list of conditions and the following disclaimer in the documentation 11 | and/or other materials provided with the distribution. 12 | * Neither the name of CM256 nor the names of its contributors may be 13 | used to endorse or promote products derived from this software without 14 | specific prior written permission. 15 | 16 | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" 17 | AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 18 | IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 19 | ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE 20 | LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR 21 | CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF 22 | SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS 23 | INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN 24 | CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) 25 | ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE 26 | POSSIBILITY OF SUCH DAMAGE. 27 | */ 28 | 29 | #include 30 | #include 31 | #include 32 | #include 33 | #include 34 | #include 35 | #include 36 | #include 37 | #include 38 | 39 | #include "mainutils.h" 40 | #include "example0.h" 41 | #include "example1.h" 42 | #include "../cm256.h" 43 | 44 | /** Flag is set on SIGINT / SIGTERM. */ 45 | static std::atomic_bool stop_flag(false); 46 | 47 | /** Handle Ctrl-C and SIGTERM. */ 48 | static void handle_sigterm(int sig) 49 | { 50 | stop_flag.store(true); 51 | 52 | std::string msg = "\nGot signal "; 53 | msg += strsignal(sig); 54 | msg += ", stopping ...\n"; 55 | 56 | const char *s = msg.c_str(); 57 | ssize_t r = write(STDERR_FILENO, s, strlen(s)); 58 | 59 | if (r != (ssize_t) strlen(s)) { 60 | msg += " write incomplete"; 61 | } 62 | } 63 | 64 | static void usage() 65 | { 66 | fprintf(stderr, 67 | "Usage: cm256_rx [options]\n" 68 | "\n" 69 | " -c case Test case index\n" 70 | " - 0: file test:\n" 71 | " -f file test file\n" 72 | " -r ref_file reference file\n" 73 | " - 1: UDP test:\n" 74 | " -I address IP address. Samples are sent to this address (default: 127.0.0.1)\n" 75 | " -p port Data port. Samples are sent on this UDP port (default 9090)\n" 76 | "\n"); 77 | } 78 | 79 | int main(int argc, char *argv[]) 80 | { 81 | int testCaseIndex = 0; 82 | std::string dataaddress("127.0.0.1"); 83 | int dataport = 9090; 84 | std::string filename("cm256.test"); 85 | std::string refFilename("cm256.ref.test"); 86 | 87 | struct sigaction action; 88 | memset(&action, 0, sizeof(struct sigaction)); 89 | action.sa_handler = handle_sigterm; 90 | sigaction(SIGTERM, &action, NULL); 91 | 92 | const struct option longopts[] = { 93 | { "case", 1, NULL, 'c' }, 94 | { "daddress", 2, NULL, 'I' }, 95 | { "dport", 1, NULL, 'P' }, 96 | { "file", 2, NULL, 'f' }, 97 | { "reffile", 2, NULL, 'r' }, 98 | { NULL, 0, NULL, 0 } }; 99 | 100 | int c, longindex, value; 101 | while ((c = getopt_long(argc, argv, 102 | "c:I:P:f:r:", 103 | longopts, &longindex)) >= 0) 104 | { 105 | switch (c) 106 | { 107 | case 'c': 108 | if (!parse_int(optarg, value) || (value < 0) || (value > 1)) { 109 | badarg("-b"); 110 | } else { 111 | testCaseIndex = value; 112 | } 113 | break; 114 | case 'I': 115 | dataaddress.assign(optarg); 116 | break; 117 | case 'P': 118 | if (!parse_int(optarg, value) || (value < 0)) { 119 | badarg("-P"); 120 | } else { 121 | dataport = value; 122 | } 123 | break; 124 | case 'f': 125 | filename.assign(optarg); 126 | break; 127 | case 'r': 128 | refFilename.assign(optarg); 129 | break; 130 | default: 131 | usage(); 132 | fprintf(stderr, "ERROR: Invalid command line options\n"); 133 | exit(1); 134 | } 135 | } 136 | 137 | if (optind < argc) 138 | { 139 | usage(); 140 | fprintf(stderr, "ERROR: Unexpected command line options\n"); 141 | exit(1); 142 | } 143 | 144 | if (testCaseIndex == 0) 145 | { 146 | std::cerr << "example0:" << std::endl; 147 | 148 | if (!example0_rx(filename, refFilename)) 149 | { 150 | std::cerr << "example0 failed" << std::endl << std::endl; 151 | return 1; 152 | } 153 | 154 | std::cerr << "example0 successful" << std::endl; 155 | } 156 | else if (testCaseIndex == 1) 157 | { 158 | std::cerr << "example1:" << std::endl; 159 | 160 | if (!example1_rx(dataaddress, (unsigned short) dataport, stop_flag)) 161 | { 162 | std::cerr << "example1 failed" << std::endl << std::endl; 163 | return 1; 164 | } 165 | 166 | std::cerr << "example1 successful" << std::endl; 167 | } 168 | 169 | return 0; 170 | } 171 | 172 | 173 | 174 | -------------------------------------------------------------------------------- /unit_test/transmit.cpp: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright (c) 2016 Edouard M. Griffiths. All rights reserved. 3 | 4 | Redistribution and use in source and binary forms, with or without 5 | modification, are permitted provided that the following conditions are met: 6 | 7 | * Redistributions of source code must retain the above copyright notice, 8 | this list of conditions and the following disclaimer. 9 | * Redistributions in binary form must reproduce the above copyright notice, 10 | this list of conditions and the following disclaimer in the documentation 11 | and/or other materials provided with the distribution. 12 | * Neither the name of CM256 nor the names of its contributors may be 13 | used to endorse or promote products derived from this software without 14 | specific prior written permission. 15 | 16 | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" 17 | AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 18 | IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 19 | ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE 20 | LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR 21 | CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF 22 | SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS 23 | INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN 24 | CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) 25 | ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE 26 | POSSIBILITY OF SUCH DAMAGE. 27 | */ 28 | 29 | #include 30 | #include 31 | #include 32 | #include 33 | #include 34 | #include 35 | #include 36 | #include 37 | #include 38 | 39 | #include "mainutils.h" 40 | #include "example0.h" 41 | #include "example1.h" 42 | 43 | /** Flag is set on SIGINT / SIGTERM. */ 44 | static std::atomic_bool stop_flag(false); 45 | 46 | /** Handle Ctrl-C and SIGTERM. */ 47 | static void handle_sigterm(int sig) 48 | { 49 | stop_flag.store(true); 50 | 51 | std::string msg = "\nGot signal "; 52 | msg += strsignal(sig); 53 | msg += ", stopping ...\n"; 54 | 55 | const char *s = msg.c_str(); 56 | ssize_t r = write(STDERR_FILENO, s, strlen(s)); 57 | 58 | if (r != (ssize_t) strlen(s)) { 59 | msg += " write incomplete"; 60 | } 61 | } 62 | 63 | static void usage() 64 | { 65 | fprintf(stderr, 66 | "Usage: cm256_tx [options]\n" 67 | "\n" 68 | " -x list Comma separated list of block index to remove from transmission\n" 69 | " this is to simulate data loss\n" 70 | " -c case Test case index\n" 71 | " - 0: file test:\n" 72 | " -f file test file\n" 73 | " -r ref_file reference file\n" 74 | " - 1: UDP test:\n" 75 | " -I address IP address. Samples are sent to this address (default: 127.0.0.1)\n" 76 | " -p port Data port. Samples are sent on this UDP port (default 9090)\n" 77 | "\n"); 78 | } 79 | 80 | int main(int argc, char *argv[]) 81 | { 82 | int testCaseIndex = 0; 83 | std::string dataaddress("127.0.0.1"); 84 | int dataport = 9090; 85 | std::string filename("cm256.test"); 86 | std::string refFilename("cm256.ref.test"); 87 | std::string blockExclusionStr; 88 | std::vector blocExclusionList; 89 | 90 | struct sigaction action; 91 | memset(&action, 0, sizeof(struct sigaction)); 92 | action.sa_handler = handle_sigterm; 93 | sigaction(SIGTERM, &action, NULL); 94 | 95 | const struct option longopts[] = { 96 | { "exlist", 2, NULL, 'x' }, 97 | { "case", 1, NULL, 'c' }, 98 | { "daddress", 2, NULL, 'I' }, 99 | { "dport", 1, NULL, 'P' }, 100 | { "file", 2, NULL, 'f' }, 101 | { "reffile", 2, NULL, 'r' }, 102 | { NULL, 0, NULL, 0 } }; 103 | 104 | int c, longindex, value; 105 | while ((c = getopt_long(argc, argv, 106 | "x:c:I:P:f:r:", 107 | longopts, &longindex)) >= 0) 108 | { 109 | switch (c) 110 | { 111 | case 'x': 112 | blockExclusionStr.assign(optarg); 113 | if (!getIntList(blocExclusionList, blockExclusionStr)) 114 | { 115 | fprintf(stderr, "ERROR: Invalid block exclusion list\n"); 116 | exit(1); 117 | } 118 | else 119 | { 120 | std::cerr << "Exclude:"; 121 | 122 | for (std::vector::iterator it = blocExclusionList.begin(); it != blocExclusionList.end(); ++it) 123 | { 124 | std::cerr << " " << *it; 125 | } 126 | 127 | std::cerr << std::endl; 128 | } 129 | break; 130 | case 'c': 131 | if (!parse_int(optarg, value) || (value < 0) || (value > 1)) 132 | { 133 | usage(); 134 | badarg("-b"); 135 | exit(1); 136 | } 137 | else 138 | { 139 | testCaseIndex = value; 140 | } 141 | break; 142 | case 'I': 143 | dataaddress.assign(optarg); 144 | break; 145 | case 'P': 146 | if (!parse_int(optarg, value) || (value < 0)) 147 | { 148 | usage(); 149 | badarg("-P"); 150 | exit(1); 151 | } 152 | else 153 | { 154 | dataport = value; 155 | } 156 | break; 157 | case 'f': 158 | filename.assign(optarg); 159 | break; 160 | case 'r': 161 | refFilename.assign(optarg); 162 | break; 163 | default: 164 | usage(); 165 | fprintf(stderr, "ERROR: Invalid command line options\n"); 166 | exit(1); 167 | } 168 | } 169 | 170 | if (optind < argc) 171 | { 172 | usage(); 173 | fprintf(stderr, "ERROR: Unexpected command line options\n"); 174 | exit(1); 175 | } 176 | 177 | if (testCaseIndex == 0) 178 | { 179 | std::cerr << "example0:" << std::endl; 180 | 181 | if (!example0_tx(filename, refFilename)) 182 | { 183 | std::cerr << "example0 failed" << std::endl << std::endl; 184 | return 1; 185 | } 186 | 187 | std::cerr << "example0 successful" << std::endl; 188 | } 189 | else if (testCaseIndex == 1) 190 | { 191 | std::cerr << "example1:" << std::endl; 192 | 193 | if (!example1_tx(dataaddress, dataport, blocExclusionList, stop_flag)) 194 | { 195 | std::cerr << "example1 failed" << std::endl << std::endl; 196 | return 1; 197 | } 198 | 199 | std::cerr << "example1 successful" << std::endl; 200 | } 201 | 202 | return 0; 203 | } 204 | 205 | 206 | --------------------------------------------------------------------------------