├── .gitignore ├── CMakeLists.txt ├── License.md ├── Logger.cpp ├── Logger.h ├── PacketAllocator.cpp ├── PacketAllocator.h ├── README.md ├── SiameseCommon.cpp ├── SiameseCommon.h ├── SiameseDecoder.cpp ├── SiameseDecoder.h ├── SiameseEncoder.cpp ├── SiameseEncoder.h ├── SiameseSerializers.h ├── SiameseTools.cpp ├── SiameseTools.h ├── gf256.cpp ├── gf256.h ├── proj └── msvc │ ├── Siamese.sln │ ├── SiameseLib.vcxproj │ └── SiameseLib.vcxproj.filters ├── siamese.cpp ├── siamese.h └── tests ├── GF256Matrix.cpp ├── GF256Matrix.h ├── TestTools.cpp ├── TestTools.h ├── _attic_ └── gentab_graycode.cpp ├── gentab_primes.cpp ├── msvc ├── SiameseInternalTests.vcxproj ├── SiameseInternalTests.vcxproj.filters ├── SiameseUnitTest.vcxproj └── SiameseUnitTest.vcxproj.filters ├── test_allocator.cpp ├── test_custom_bitset.cpp ├── test_invert_rate.cpp ├── test_recovery_sort.cpp ├── test_serializers.cpp └── unit_test.cpp /.gitignore: -------------------------------------------------------------------------------- 1 | ## Ignore Visual Studio temporary files, build results, and 2 | ## files generated by popular Visual Studio add-ons. 3 | 4 | # User-specific files 5 | *.suo 6 | *.user 7 | *.userosscache 8 | *.sln.docstates 9 | 10 | # User-specific files (MonoDevelop/Xamarin Studio) 11 | *.userprefs 12 | 13 | # Build results 14 | [Dd]ebug/ 15 | [Dd]ebugPublic/ 16 | [Rr]elease/ 17 | [Rr]eleases/ 18 | x64/ 19 | x86/ 20 | bld/ 21 | [Bb]in/ 22 | [Oo]bj/ 23 | [Ll]og/ 24 | 25 | # Visual Studio 2015 cache/options directory 26 | .vs/ 27 | # Uncomment if you have tasks that create the project's static files in wwwroot 28 | #wwwroot/ 29 | 30 | # MSTest test Results 31 | [Tt]est[Rr]esult*/ 32 | [Bb]uild[Ll]og.* 33 | 34 | # NUNIT 35 | *.VisualState.xml 36 | TestResult.xml 37 | 38 | # Build Results of an ATL Project 39 | [Dd]ebugPS/ 40 | [Rr]eleasePS/ 41 | dlldata.c 42 | 43 | # DNX 44 | project.lock.json 45 | artifacts/ 46 | 47 | *_i.c 48 | *_p.c 49 | *_i.h 50 | *.ilk 51 | *.meta 52 | *.obj 53 | *.pch 54 | *.pdb 55 | *.pgc 56 | *.pgd 57 | *.rsp 58 | *.sbr 59 | *.tlb 60 | *.tli 61 | *.tlh 62 | *.tmp 63 | *.tmp_proj 64 | *.log 65 | *.vspscc 66 | *.vssscc 67 | .builds 68 | *.pidb 69 | *.svclog 70 | *.scc 71 | 72 | # Chutzpah Test files 73 | _Chutzpah* 74 | 75 | # Visual C++ cache files 76 | ipch/ 77 | *.aps 78 | *.ncb 79 | *.opendb 80 | *.opensdf 81 | *.sdf 82 | *.cachefile 83 | *.VC.db 84 | *.VC.VC.opendb 85 | 86 | # Visual Studio profiler 87 | *.psess 88 | *.vsp 89 | *.vspx 90 | *.sap 91 | 92 | # TFS 2012 Local Workspace 93 | $tf/ 94 | 95 | # Guidance Automation Toolkit 96 | *.gpState 97 | 98 | # ReSharper is a .NET coding add-in 99 | _ReSharper*/ 100 | *.[Rr]e[Ss]harper 101 | *.DotSettings.user 102 | 103 | # JustCode is a .NET coding add-in 104 | .JustCode 105 | 106 | # TeamCity is a build add-in 107 | _TeamCity* 108 | 109 | # DotCover is a Code Coverage Tool 110 | *.dotCover 111 | 112 | # NCrunch 113 | _NCrunch_* 114 | .*crunch*.local.xml 115 | nCrunchTemp_* 116 | 117 | # MightyMoose 118 | *.mm.* 119 | AutoTest.Net/ 120 | 121 | # Web workbench (sass) 122 | .sass-cache/ 123 | 124 | # Installshield output folder 125 | [Ee]xpress/ 126 | 127 | # DocProject is a documentation generator add-in 128 | DocProject/buildhelp/ 129 | DocProject/Help/*.HxT 130 | DocProject/Help/*.HxC 131 | DocProject/Help/*.hhc 132 | DocProject/Help/*.hhk 133 | DocProject/Help/*.hhp 134 | DocProject/Help/Html2 135 | DocProject/Help/html 136 | 137 | # Click-Once directory 138 | publish/ 139 | 140 | # Publish Web Output 141 | *.[Pp]ublish.xml 142 | *.azurePubxml 143 | # TODO: Comment the next line if you want to checkin your web deploy settings 144 | # but database connection strings (with potential passwords) will be unencrypted 145 | *.pubxml 146 | *.publishproj 147 | 148 | # Microsoft Azure Web App publish settings. Comment the next line if you want to 149 | # checkin your Azure Web App publish settings, but sensitive information contained 150 | # in these scripts will be unencrypted 151 | PublishScripts/ 152 | 153 | # NuGet Packages 154 | *.nupkg 155 | # The packages folder can be ignored because of Package Restore 156 | **/packages/* 157 | # except build/, which is used as an MSBuild target. 158 | !**/packages/build/ 159 | # Uncomment if necessary however generally it will be regenerated when needed 160 | #!**/packages/repositories.config 161 | # NuGet v3's project.json files produces more ignoreable files 162 | *.nuget.props 163 | *.nuget.targets 164 | 165 | # Microsoft Azure Build Output 166 | csx/ 167 | *.build.csdef 168 | 169 | # Microsoft Azure Emulator 170 | ecf/ 171 | rcf/ 172 | 173 | # Windows Store app package directories and files 174 | AppPackages/ 175 | BundleArtifacts/ 176 | Package.StoreAssociation.xml 177 | _pkginfo.txt 178 | 179 | # Visual Studio cache files 180 | # files ending in .cache can be ignored 181 | *.[Cc]ache 182 | # but keep track of directories ending in .cache 183 | !*.[Cc]ache/ 184 | 185 | # Others 186 | ClientBin/ 187 | ~$* 188 | *~ 189 | *.dbmdl 190 | *.dbproj.schemaview 191 | *.pfx 192 | *.publishsettings 193 | node_modules/ 194 | orleans.codegen.cs 195 | 196 | # Since there are multiple workflows, uncomment next line to ignore bower_components 197 | # (https://github.com/github/gitignore/pull/1529#issuecomment-104372622) 198 | #bower_components/ 199 | 200 | # RIA/Silverlight projects 201 | Generated_Code/ 202 | 203 | # Backup & report files from converting an old project file 204 | # to a newer Visual Studio version. Backup files are not needed, 205 | # because we have git ;-) 206 | _UpgradeReport_Files/ 207 | Backup*/ 208 | UpgradeLog*.XML 209 | UpgradeLog*.htm 210 | 211 | # SQL Server files 212 | *.mdf 213 | *.ldf 214 | 215 | # Business Intelligence projects 216 | *.rdl.data 217 | *.bim.layout 218 | *.bim_*.settings 219 | 220 | # Microsoft Fakes 221 | FakesAssemblies/ 222 | 223 | # GhostDoc plugin setting file 224 | *.GhostDoc.xml 225 | 226 | # Node.js Tools for Visual Studio 227 | .ntvs_analysis.dat 228 | 229 | # Visual Studio 6 build log 230 | *.plg 231 | 232 | # Visual Studio 6 workspace options file 233 | *.opt 234 | 235 | # Visual Studio LightSwitch build output 236 | **/*.HTMLClient/GeneratedArtifacts 237 | **/*.DesktopClient/GeneratedArtifacts 238 | **/*.DesktopClient/ModelManifest.xml 239 | **/*.Server/GeneratedArtifacts 240 | **/*.Server/ModelManifest.xml 241 | _Pvt_Extensions 242 | 243 | # Paket dependency manager 244 | .paket/paket.exe 245 | paket-files/ 246 | 247 | # FAKE - F# Make 248 | .fake/ 249 | 250 | # JetBrains Rider 251 | .idea/ 252 | *.sln.iml 253 | lib/cauchy.txt 254 | *.txt 255 | lib/Siamese.dll 256 | *.lib 257 | lib/SiameseUnitTests.exe 258 | lib/SiameseUnitTests.exe 259 | *.exe 260 | lib/SiameseUnitTests.exe 261 | -------------------------------------------------------------------------------- /CMakeLists.txt: -------------------------------------------------------------------------------- 1 | cmake_minimum_required(VERSION 3.5) 2 | project(siamese LANGUAGES C CXX) 3 | 4 | 5 | ################################################################################ 6 | # Configuration 7 | 8 | # Verbose compilation? 9 | #set(CMAKE_VERBOSE_MAKEFILE ON) 10 | 11 | 12 | ################################################################################ 13 | # Build Settings 14 | 15 | set(CMAKE_CONFIGURATION_TYPES "Debug;Release" CACHE STRING "" FORCE) 16 | 17 | if(NOT CMAKE_BUILD_TYPE) 18 | set(CMAKE_BUILD_TYPE Release) 19 | endif() 20 | 21 | # In debug mode, add -DDEBUG 22 | add_compile_options("$<$:-DDEBUG>") 23 | 24 | set(CMAKE_CXX_STANDARD 11) 25 | set(CMAKE_CXX_STANDARD 14) 26 | 27 | if(MSVC) 28 | set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} /W4") 29 | else() 30 | # Warnings 31 | set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -Wall -Wextra -pedantic") 32 | 33 | # Remove Asio warning 34 | set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -Wno-unused-local-typedefs") 35 | 36 | # Static library: -fPIC 37 | set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -fPIC") 38 | 39 | # Gprof Profiling 40 | #set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -pg") 41 | 42 | set(CMAKE_CXX_FLAGS_DEBUG "-g -O0 -march=native -fstack-protector") 43 | set(CMAKE_CXX_FLAGS_RELEASE "-O3 -march=native") 44 | endif() 45 | 46 | #include_directories(include) 47 | 48 | 49 | ################################################################################ 50 | # Dependencies 51 | 52 | set(THREADS_PREFER_PTHREAD_FLAG ON) 53 | find_package(Threads REQUIRED) 54 | 55 | 56 | ################################################################################ 57 | # Source Files 58 | 59 | set(INCLUDE_FILES 60 | gf256.h 61 | Logger.h 62 | PacketAllocator.h 63 | SiameseCommon.h 64 | SiameseDecoder.h 65 | SiameseEncoder.h 66 | siamese.h 67 | SiameseSerializers.h 68 | SiameseTools.h 69 | ) 70 | set(SOURCE_FILES 71 | ${INCLUDE_FILES} 72 | gf256.cpp 73 | Logger.cpp 74 | PacketAllocator.cpp 75 | SiameseCommon.cpp 76 | siamese.cpp 77 | SiameseDecoder.cpp 78 | SiameseEncoder.cpp 79 | SiameseTools.cpp 80 | #tests/gentab_primes.cpp 81 | #tests/GF256Matrix.cpp 82 | #tests/GF256Matrix.h 83 | #tests/test_allocator.cpp 84 | #tests/test_custom_bitset.cpp 85 | #tests/test_invert_rate.cpp 86 | #tests/test_recovery_sort.cpp 87 | #tests/test_serializers.cpp 88 | tests/TestTools.cpp 89 | tests/TestTools.h 90 | tests/unit_test.cpp 91 | ) 92 | 93 | 94 | ################################################################################ 95 | # Targets 96 | 97 | # Application 98 | 99 | add_executable(unit_test ${SOURCE_FILES}) 100 | target_link_libraries(unit_test 101 | Threads::Threads 102 | ) 103 | install(TARGETS unit_test DESTINATION bin) 104 | -------------------------------------------------------------------------------- /License.md: -------------------------------------------------------------------------------- 1 | BSD 3-Clause License 2 | 3 | Copyright (c) 2017, Christopher A. Taylor 4 | All rights reserved. 5 | 6 | Redistribution and use in source and binary forms, with or without 7 | modification, are permitted provided that the following conditions are met: 8 | 9 | * Redistributions of source code must retain the above copyright notice, this 10 | list of conditions and the following disclaimer. 11 | 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 | 16 | * Neither the name of the copyright holder nor the names of its 17 | contributors may be used to endorse or promote products derived from 18 | this software without specific prior written permission. 19 | 20 | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" 21 | AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 22 | IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE 23 | DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE 24 | FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 25 | DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR 26 | SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER 27 | CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, 28 | OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE 29 | OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 30 | -------------------------------------------------------------------------------- /Logger.cpp: -------------------------------------------------------------------------------- 1 | /** \file 2 | \brief Logging Module 3 | \copyright Copyright (c) 2017 Christopher A. Taylor. All rights reserved. 4 | 5 | Redistribution and use in source and binary forms, with or without 6 | modification, are permitted provided that the following conditions are met: 7 | 8 | * Redistributions of source code must retain the above copyright notice, 9 | this list of conditions and the following disclaimer. 10 | * Redistributions in binary form must reproduce the above copyright notice, 11 | this list of conditions and the following disclaimer in the documentation 12 | and/or other materials provided with the distribution. 13 | * Neither the name of Logger nor the names of its contributors may be 14 | used to endorse or promote products derived from this software without 15 | specific prior written permission. 16 | 17 | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" 18 | AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 19 | IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 20 | ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE 21 | LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR 22 | CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF 23 | SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS 24 | INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN 25 | CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) 26 | ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE 27 | POSSIBILITY OF SUCH DAMAGE. 28 | */ 29 | 30 | #include "Logger.h" 31 | 32 | #if !defined(ANDROID) 33 | #include // fwrite, stdout 34 | #endif 35 | 36 | #if defined(_WIN32) 37 | #if !defined(NOMINMAX) 38 | #define NOMINMAX 39 | #endif 40 | #include // OutputDebugStringA 41 | #endif 42 | 43 | 44 | namespace logger { 45 | 46 | 47 | //------------------------------------------------------------------------------ 48 | // Level 49 | 50 | static const char* kLevelStrings[(int)Level::Count] = { 51 | "Trace", "Debug", "Info", "Warning", "Error", "Silent" 52 | }; 53 | 54 | const char* LevelToString(Level level) 55 | { 56 | static_assert((int)Level::Count == 6, "Update this switch"); 57 | LOGGER_DEBUG_ASSERT((int)level >= 0 && level < Level::Count); 58 | return kLevelStrings[(int)level]; 59 | } 60 | 61 | static const char kLevelChars[(int)Level::Count] = { 62 | 't', 'd', 'I', 'W', '!', '?' 63 | }; 64 | 65 | char LevelToChar(Level level) 66 | { 67 | static_assert((int)Level::Count == 6, "Update this switch"); 68 | LOGGER_DEBUG_ASSERT((int)level >= 0 && level < Level::Count); 69 | return kLevelChars[(int)level]; 70 | } 71 | 72 | 73 | //------------------------------------------------------------------------------ 74 | // OutputWorker 75 | 76 | OutputWorker& OutputWorker::GetInstance() 77 | { 78 | // Fix crash on shutdown due to MSVCRT bug prior to version 2015 79 | static std::once_flag m_singletonFlag; 80 | static std::unique_ptr instance; 81 | 82 | std::call_once(m_singletonFlag, []() 83 | { 84 | instance.reset(new OutputWorker); 85 | }); 86 | 87 | return *instance.get(); 88 | } 89 | 90 | #if !defined(LOGGER_DISABLE_ATEXIT) 91 | static void AtExitWrapper() 92 | { 93 | OutputWorker::GetInstance().Stop(); 94 | } 95 | #endif // LOGGER_DISABLE_ATEXIT 96 | 97 | OutputWorker::OutputWorker() 98 | { 99 | Start(); 100 | 101 | #if !defined(LOGGER_DISABLE_ATEXIT) 102 | // Register an atexit() callback so we do not need manual shutdown in app code 103 | // Application code can still manually shutdown by calling OutputWorker::Stop() 104 | std::atexit(AtExitWrapper); 105 | #endif // LOGGER_DISABLE_ATEXIT 106 | } 107 | 108 | void OutputWorker::Start() 109 | { 110 | std::lock_guard locker(StartStopLock); 111 | 112 | // If thread is already spinning: 113 | if (Thread) 114 | return; 115 | 116 | #if defined(_WIN32) 117 | CachedIsDebuggerPresent = (::IsDebuggerPresent() != FALSE); 118 | #endif // _WIN32 119 | 120 | QueuePublic.clear(); 121 | QueuePrivate.clear(); 122 | 123 | #if !defined(LOGGER_NEVER_DROP) 124 | Overrun = 0; 125 | #endif // LOGGER_NEVER_DROP 126 | FlushRequested = false; 127 | Terminated = false; 128 | Thread = std::make_shared(&OutputWorker::Loop, this); 129 | 130 | #if defined(_WIN32) 131 | ThreadNativeHandle = Thread->native_handle(); 132 | #endif // _WIN32 133 | } 134 | 135 | void OutputWorker::Stop() 136 | { 137 | std::lock_guard locker(StartStopLock); 138 | 139 | if (Thread) 140 | { 141 | #if defined(_WIN32) 142 | // If thread was force-terminated by something: 143 | if (WAIT_OBJECT_0 == ::WaitForSingleObject(ThreadNativeHandle, 0)) 144 | { 145 | LOGGER_DEBUG_BREAK(); // Application force-terminated the thread 146 | return; // Abort shutdown... 147 | } 148 | #endif // _WIN32 149 | 150 | Flush(); 151 | 152 | Terminated = true; 153 | 154 | // Make sure that queue notification happens after termination flag is set 155 | { 156 | std::unique_lock qlocker(QueueLock); 157 | QueueCondition.notify_all(); 158 | } 159 | 160 | try 161 | { 162 | if (Thread->joinable()) 163 | Thread->join(); 164 | } 165 | catch (std::system_error& /*err*/) 166 | { 167 | } 168 | } 169 | Thread = nullptr; 170 | 171 | // Make sure that concurrent Flush() calls do not block 172 | { 173 | std::unique_lock qlocker(QueueLock); 174 | FlushCondition.notify_all(); 175 | } 176 | } 177 | 178 | void OutputWorker::Write(LogStringBuffer& buffer) 179 | { 180 | std::string str = buffer.LogStream.str(); 181 | 182 | #if defined(_WIN32) 183 | // If a debugger is present: 184 | if (CachedIsDebuggerPresent) 185 | { 186 | // Log all messages immediately to the Visual Studio Output Window 187 | // to allow logging while single-stepping in a debugger. 188 | ::OutputDebugStringA((str + "\n").c_str()); 189 | } 190 | #endif // _WIN32 191 | 192 | #if defined(LOGGER_NEVER_DROP) 193 | for (;; Flush()) 194 | { 195 | std::lock_guard locker(QueueLock); 196 | 197 | if (QueuePublic.size() >= kWorkQueueLimit) 198 | { 199 | continue; 200 | } 201 | else 202 | { 203 | QueuePublic.emplace_back(buffer.LogLevel, buffer.ChannelName, str); 204 | break; 205 | } 206 | } 207 | #else // LOGGER_NEVER_DROP 208 | { 209 | std::lock_guard locker(QueueLock); 210 | 211 | if (QueuePublic.size() >= kWorkQueueLimit) 212 | Overrun++; 213 | else 214 | QueuePublic.emplace_back(buffer.LogLevel, buffer.ChannelName, str); 215 | } 216 | #endif // LOGGER_NEVER_DROP 217 | 218 | QueueCondition.notify_all(); 219 | } 220 | 221 | void OutputWorker::Loop() 222 | { 223 | while (!Terminated) 224 | { 225 | int overrun = 0; 226 | bool flushRequested = false; 227 | { 228 | // unique_lock used since QueueCondition.wait requires it 229 | std::unique_lock locker(QueueLock); 230 | 231 | if (QueuePublic.empty() && !FlushRequested && !Terminated) 232 | QueueCondition.wait(locker); 233 | 234 | std::swap(QueuePublic, QueuePrivate); 235 | 236 | #if !defined(LOGGER_NEVER_DROP) 237 | overrun = Overrun; 238 | Overrun = 0; 239 | #endif // LOGGER_NEVER_DROP 240 | 241 | flushRequested = FlushRequested; 242 | FlushRequested = false; 243 | } 244 | 245 | for (auto& log : QueuePrivate) 246 | Log(log); 247 | 248 | // Handle log message overrun 249 | if (overrun > 0) 250 | { 251 | std::ostringstream oss; 252 | oss << "Queue overrun. Lost " << overrun << " log messages"; 253 | std::string str = oss.str(); 254 | QueuedMessage qm(Level::Error, "Logger", str); 255 | Log(qm); 256 | } 257 | 258 | QueuePrivate.clear(); 259 | 260 | if (flushRequested) 261 | FlushCondition.notify_all(); 262 | } 263 | 264 | // Log out that logger is terminating 265 | std::string terminatingMessage = "Terminating"; 266 | QueuedMessage qm(Level::Info, "Logger", terminatingMessage); 267 | Log(qm); 268 | } 269 | 270 | void OutputWorker::Log(QueuedMessage& message) 271 | { 272 | std::ostringstream ss; 273 | ss << '{' << LevelToChar(message.LogLevel) << '-' << message.ChannelName << "} " << message.Message; 274 | 275 | #if defined(ANDROID) 276 | std::string fmtstr = ss.str(); 277 | __android_log_print(ANDROID_LOG_ERROR, LOG_TAG, "%s", fmtstr.c_str()); 278 | #else // ANDROID 279 | ss << std::endl; 280 | std::string fmtstr = ss.str(); 281 | fwrite(fmtstr.c_str(), 1, fmtstr.size(), stdout); 282 | #endif // ANDROID 283 | } 284 | 285 | void OutputWorker::Flush() 286 | { 287 | // unique_lock used since FlushCondition.wait requires it 288 | std::unique_lock locker(QueueLock); 289 | 290 | if (!Terminated) 291 | { 292 | FlushRequested = true; 293 | QueueCondition.notify_all(); 294 | 295 | FlushCondition.wait(locker); 296 | } 297 | } 298 | 299 | 300 | //------------------------------------------------------------------------------ 301 | // Channel 302 | 303 | Channel::Channel(const char* name, Level minLevel) 304 | : ChannelName(name) 305 | , ChannelMinLevel(minLevel) 306 | { 307 | } 308 | 309 | std::string Channel::GetPrefix() const 310 | { 311 | std::lock_guard locker(PrefixLock); 312 | return Prefix; 313 | } 314 | 315 | void Channel::SetPrefix(const std::string& prefix) 316 | { 317 | std::lock_guard locker(PrefixLock); 318 | Prefix = prefix; 319 | } 320 | 321 | 322 | } // namespace logger 323 | 324 | 325 | // Fix hang on shutdown due to MSVCRT bug prior to version 2015 326 | #if defined(_MSC_VER) && (_MSC_VER < 1900) 327 | 328 | // https://stackoverflow.com/questions/10915233/stdthreadjoin-hangs-if-called-after-main-exits-when-using-vs2012-rc 329 | #pragma warning(disable:4073) // initializers put in library initialization area 330 | #pragma init_seg(lib) 331 | 332 | struct VS2013_threading_fix 333 | { 334 | VS2013_threading_fix() 335 | { 336 | _Cnd_do_broadcast_at_thread_exit(); 337 | } 338 | } threading_fix; 339 | 340 | #endif // _MSC_VER 341 | -------------------------------------------------------------------------------- /Logger.h: -------------------------------------------------------------------------------- 1 | /** \file 2 | \brief Logging Module 3 | \copyright Copyright (c) 2017 Christopher A. Taylor. All rights reserved. 4 | 5 | Redistribution and use in source and binary forms, with or without 6 | modification, are permitted provided that the following conditions are met: 7 | 8 | * Redistributions of source code must retain the above copyright notice, 9 | this list of conditions and the following disclaimer. 10 | * Redistributions in binary form must reproduce the above copyright notice, 11 | this list of conditions and the following disclaimer in the documentation 12 | and/or other materials provided with the distribution. 13 | * Neither the name of Logger nor the names of its contributors may be 14 | used to endorse or promote products derived from this software without 15 | specific prior written permission. 16 | 17 | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" 18 | AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 19 | IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 20 | ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE 21 | LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR 22 | CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF 23 | SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS 24 | INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN 25 | CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) 26 | ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE 27 | POSSIBILITY OF SUCH DAMAGE. 28 | */ 29 | 30 | #pragma once 31 | 32 | /** \page Logger Logging Module 33 | 34 | Feature-rich portable C++ logging subsystem in 650 lines of code. 35 | The library is self-contained and is easy to incorporate into iOS, 36 | Android, Windows, Linux, and Mac projects. 37 | 38 | Quick Start guide: 39 | 40 | // Example logging channel 41 | static logger::Channel Logger("Decoder", logger::Level::Debug); 42 | 43 | // Log out a message 44 | Logger.Info("Got first recovery packet: ColumnStart=", 45 | metadata.ColumnStart, " SumCount=", metadata.SumCount); 46 | 47 | 48 | After trying several open-source loggers I ended up writing my own. 49 | I was mainly looking for a multithreaded logger supporting levels, 50 | and found a lot of buggy software that was hard to use or missing features. 51 | 52 | Primary Logger features: 53 | 54 | * Automatic initialization and shutdown just like 'cout'. 55 | * Low performance impact since the logging occurs on a background thread. 56 | * Automatically flushes message queue on shutdown. (And it isn't buggy.) 57 | 58 | Additional extra features: 59 | 60 | * Logging Channel objects to sort messages by subsystem. 61 | * Logging Levels enabling runtime filtering of messages and faster offline analysis. 62 | * Serialization overloads in application code via LogStringize() overrides. 63 | * Supports runtime-configurable prefix tags for each Channel. 64 | 65 | On Android it uses __android_log_print(). 66 | On other platforms it uses std::fwrite(stdout). 67 | On Windows it also uses OutputDebugStringA(). 68 | 69 | Modify this function to change how it writes output: 70 | OutputWorker::Log() 71 | */ 72 | 73 | #include 74 | #include 75 | #include 76 | #include 77 | #include 78 | #include 79 | #include 80 | 81 | 82 | /// Define this to prevent the logger from hooking atexit(). 83 | /// The application must manually call logger::Stop(). 84 | //#define LOGGER_DISABLE_ATEXIT 85 | 86 | /** 87 | This will flush before and after errors, so errors can never get lost. 88 | A disadvantage is that if the console is hung for some reason, then the 89 | logger call sites will hang too, and it will also cause error logging to 90 | run much slower than normal. This is only recommended if software is 91 | written to the principle that errors are actually errors. 92 | */ 93 | //#define LOGGER_FLUSH_ERRORS 94 | 95 | // Compiler-specific force inline keyword 96 | #if defined(_MSC_VER) 97 | #define LOGGER_FORCE_INLINE inline __forceinline 98 | #else 99 | #define LOGGER_FORCE_INLINE inline __attribute__((always_inline)) 100 | #endif 101 | 102 | // Compiler-specific debug break 103 | #if defined(_DEBUG) || defined(DEBUG) 104 | #define LOGGER_DEBUG 105 | #if defined(_WIN32) 106 | #define LOGGER_DEBUG_BREAK() __debugbreak() 107 | #else 108 | #define LOGGER_DEBUG_BREAK() __builtin_trap() 109 | #endif 110 | #define LOGGER_DEBUG_ASSERT(cond) { if (!(cond)) { LOGGER_DEBUG_BREAK(); } } 111 | #else 112 | #define LOGGER_DEBUG_BREAK() do {} while (false); 113 | #define LOGGER_DEBUG_ASSERT(cond) do {} while (false); 114 | #endif 115 | 116 | 117 | namespace logger { 118 | 119 | 120 | //------------------------------------------------------------------------------ 121 | // Configuration 122 | 123 | /** 124 | Message queue overflow behavior: 125 | 126 | If messages are logged faster than we can write them to the console, then 127 | after the queue grows beyond kWorkQueueLimit messages it will drop data and 128 | write how many were dropped. The logger prefers to lose messages rather 129 | than affect performance of the application software, by default. 130 | Errors bypass this limit and will force a Flush, so errors are always logged. 131 | 132 | When LOGGER_NEVER_DROP is defined, the Logger will Flush() and retry when 133 | the queue becomes full, so the application will stall rather than lose data. 134 | 135 | When LOGGER_NEVER_DROP is not defined, the application software can call 136 | Flush() after a complex operation to make sure that new log messages land. 137 | */ 138 | //#define LOGGER_NEVER_DROP 139 | 140 | /// Tune the number of work queue items before we drop log messages on the floor. 141 | /// If LOGGER_NEVER_DROP is defined this is when we block and flush. 142 | static const size_t kWorkQueueLimit = 1024; 143 | 144 | 145 | //------------------------------------------------------------------------------ 146 | // Level 147 | 148 | /// Logging level 149 | enum class Level 150 | { 151 | Trace, ///< Trace-level logging (off by default) 152 | Debug, ///< Debug logging (on by default) 153 | Info, ///< Info (normal) logging 154 | Warning, ///< Warnings 155 | Error, ///< Errors 156 | Silent, ///< Silent level (always off) 157 | 158 | Count ///< For static assert 159 | }; 160 | 161 | /// Stringize level values 162 | const char* LevelToString(Level level); 163 | char LevelToChar(Level level); 164 | 165 | 166 | //------------------------------------------------------------------------------ 167 | // Buffer 168 | 169 | struct LogStringBuffer 170 | { 171 | const char* ChannelName; 172 | Level LogLevel; 173 | std::ostringstream LogStream; 174 | 175 | LogStringBuffer(const char* channel, Level level) : 176 | ChannelName(channel), 177 | LogLevel(level), 178 | LogStream() 179 | { 180 | } 181 | }; 182 | 183 | 184 | //------------------------------------------------------------------------------ 185 | // Stringize 186 | 187 | template 188 | LOGGER_FORCE_INLINE void LogStringize(LogStringBuffer& buffer, const T& first) 189 | { 190 | buffer.LogStream << first; 191 | } 192 | 193 | /// Overrides for various types we want to handle specially: 194 | 195 | template<> 196 | LOGGER_FORCE_INLINE void LogStringize(LogStringBuffer& buffer, const bool& first) 197 | { 198 | buffer.LogStream << (first ? "true" : "false"); 199 | } 200 | 201 | 202 | //------------------------------------------------------------------------------ 203 | // OutputWorker 204 | 205 | class OutputWorker 206 | { 207 | /// Singleton pattern 208 | explicit OutputWorker(); 209 | OutputWorker& operator=(const OutputWorker&) = delete; 210 | 211 | public: 212 | /// Get singleton instance 213 | static OutputWorker& GetInstance(); 214 | 215 | /// Manually start the output worker. This is not required 216 | void Start(); 217 | 218 | /// Manually stop the output worker. This is also not required 219 | void Stop(); 220 | 221 | /// Manually flush any items in the work queue and block here until the queue is flushed 222 | void Flush(); 223 | 224 | /// Write a log message. Use logger::Channel rather than calling this directly 225 | void Write(LogStringBuffer& buffer); 226 | 227 | protected: 228 | struct QueuedMessage 229 | { 230 | Level LogLevel; 231 | const char* ChannelName; 232 | std::string Message; 233 | 234 | QueuedMessage(Level level, const char* channel, std::string& message) 235 | : LogLevel(level) 236 | , ChannelName(channel) 237 | , Message(message) 238 | { 239 | } 240 | }; 241 | 242 | 243 | /// Lock preventing thread safety issues around Start() and Stop() 244 | mutable std::mutex StartStopLock; 245 | 246 | /// Lock protecting QueuePublic and QueueCondition 247 | mutable std::mutex QueueLock; 248 | 249 | /// Condition that indicates the thread should wake up 250 | std::condition_variable QueueCondition; 251 | 252 | /// List of messages that are being delivered to thread 253 | std::list QueuePublic; 254 | 255 | /// Private list of messages being logged out by thread 256 | std::list QueuePrivate; 257 | 258 | #if !defined(LOGGER_NEVER_DROP) 259 | /// Number of log queue overruns 260 | std::atomic Overrun = ATOMIC_VAR_INIT(0); 261 | #endif // LOGGER_NEVER_DROP 262 | 263 | /// Has a flush been requested? 264 | std::atomic FlushRequested = ATOMIC_VAR_INIT(false); 265 | 266 | /// Condition variable to wait for Flush result 267 | std::condition_variable FlushCondition; 268 | 269 | /// Queue processing thread 270 | std::shared_ptr Thread; 271 | 272 | /// Should thread terminate? 273 | std::atomic Terminated = ATOMIC_VAR_INIT(true); 274 | 275 | #if defined(_WIN32) 276 | bool CachedIsDebuggerPresent = false; 277 | void* ThreadNativeHandle = nullptr; 278 | #endif // _WIN32 279 | 280 | 281 | /// Queue processing loop 282 | void Loop(); 283 | 284 | /// Internal log message dispatch function 285 | void Log(QueuedMessage& message); 286 | }; 287 | 288 | 289 | //------------------------------------------------------------------------------ 290 | // Channel 291 | 292 | /// Logging channel object: Each instance is given a channel name, and then the 293 | /// logging channel is used to output log messages. 294 | class Channel 295 | { 296 | public: 297 | /// Specify channel name and minimum output level (Level::Silent for silent) 298 | explicit Channel(const char* name, Level minLevel); 299 | 300 | 301 | /// Channel name 302 | const char* const ChannelName; 303 | 304 | /// Minimum channel log level 305 | const Level ChannelMinLevel; 306 | 307 | 308 | /// Should we log at this level? 309 | LOGGER_FORCE_INLINE bool ShouldLog(Level level) const 310 | { 311 | return level >= ChannelMinLevel; 312 | } 313 | 314 | 315 | /// Thread-safe get or set runtime-selected prefix for the logging channel 316 | std::string GetPrefix() const; 317 | void SetPrefix(const std::string& prefix); 318 | 319 | 320 | /// Log a message at a specified level 321 | template 322 | LOGGER_FORCE_INLINE void Log(Level level, Args&&... args) const 323 | { 324 | if (ShouldLog(level)) 325 | writeLogLine(level, std::forward(args)...); 326 | } 327 | 328 | /// Log an Error level message 329 | template 330 | LOGGER_FORCE_INLINE void Error(Args&&... args) const 331 | { 332 | #if !defined(LOGGER_NEVER_DROP) && defined(LOGGER_FLUSH_ERRORS) 333 | OutputWorker::GetInstance().Flush(); 334 | #endif // LOGGER_NEVER_DROP 335 | 336 | Log(Level::Error, std::forward(args)...); 337 | 338 | #if !defined(LOGGER_NEVER_DROP) && defined(LOGGER_FLUSH_ERRORS) 339 | OutputWorker::GetInstance().Flush(); 340 | #endif // LOGGER_NEVER_DROP 341 | } 342 | 343 | /// Log a Warning level message 344 | template 345 | LOGGER_FORCE_INLINE void Warning(Args&&... args) const 346 | { 347 | Log(Level::Warning, std::forward(args)...); 348 | } 349 | 350 | /// Log an Info level message 351 | template 352 | LOGGER_FORCE_INLINE void Info(Args&&... args) const 353 | { 354 | Log(Level::Info, std::forward(args)...); 355 | } 356 | 357 | /// Log a Debug level message 358 | template 359 | LOGGER_FORCE_INLINE void Debug(Args&&... args) const 360 | { 361 | Log(Level::Debug, std::forward(args)...); 362 | } 363 | 364 | /// Log a Trace level message 365 | template 366 | LOGGER_FORCE_INLINE void Trace(Args&&... args) const 367 | { 368 | Log(Level::Trace, std::forward(args)...); 369 | } 370 | 371 | protected: 372 | /// Runtime-selected channel prefix 373 | mutable std::mutex PrefixLock; 374 | std::string Prefix; 375 | 376 | 377 | template 378 | LOGGER_FORCE_INLINE void writeLogBuffer(LogStringBuffer& buffer, T&& arg) const 379 | { 380 | LogStringize(buffer, arg); 381 | } 382 | 383 | template 384 | LOGGER_FORCE_INLINE void writeLogBuffer(LogStringBuffer& buffer, T&& arg, Args&&... args) const 385 | { 386 | writeLogBuffer(buffer, arg); 387 | writeLogBuffer(buffer, args...); 388 | } 389 | 390 | template 391 | LOGGER_FORCE_INLINE void writeLogLine(Level level, Args&&... args) const 392 | { 393 | LogStringBuffer buffer(ChannelName, level); 394 | writeLogBuffer(buffer, Prefix, args...); 395 | OutputWorker::GetInstance().Write(buffer); 396 | } 397 | }; 398 | 399 | /// Flush log output to console 400 | LOGGER_FORCE_INLINE void Flush() 401 | { 402 | OutputWorker::GetInstance().Flush(); 403 | } 404 | 405 | /// [Re-]Start the logger 406 | LOGGER_FORCE_INLINE void Start() 407 | { 408 | OutputWorker::GetInstance().Start(); 409 | } 410 | 411 | /// Stop the logger 412 | LOGGER_FORCE_INLINE void Stop() 413 | { 414 | OutputWorker::GetInstance().Stop(); 415 | } 416 | 417 | 418 | } // namespace logger 419 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Siamese 2 | ## Fast and Portable Real-Time Streaming Erasure Correction Codes in C 3 | 4 | Siamese is a fast and portable library for Erasure Correction. 5 | From a real-time stream of input data it generates redundant data that can be used to 6 | recover the lost originals without using acknowledgements. 7 | 8 | Siamese is a streaming erasure code designed for low to medium rate streams 9 | under 2000 packets per RTT (~3MB/s on the Internet), and modest loss rates 10 | like 20% or less. It works on mobile phones and mac/linux/windows computers. 11 | 12 | 13 | ##### What's unique about this approach? 14 | 15 | This library implements a new type of convolutional code for erasure correction. 16 | Since it is not a block code, as soon as recovery data arrives it can be used to recover original data. 17 | The advantages this provides are explained thoroughly in this article: 18 | 19 | [Block or Convolutional AL-FEC Codes? A Performance 20 | Comparison for Robust Low-Latency Communications](https://hal.inria.fr/hal-01395937v2/document) [1]. 21 | 22 | ~~~ 23 | [1] Vincent Roca, Belkacem Teibi, Christophe Burdinat, Tuan Tran-Thai, C´edric Thienot. Block 24 | or Convolutional AL-FEC Codes? A Performance Comparison for Robust Low-Latency Communications. 25 | 2017. 26 | ~~~ 27 | 28 | For each recovery symbol that it produces, it stores and reuses some of the intermediate work, so that producing the next symbol takes much less time than usual. As a side-effect, this approach only works for full (not partial) reliable data delivery. 29 | 30 | There is also a block code (rather than streaming) implementation here: [fecal](https://github.com/catid/fecal). It's easier to understand the new convolutional code math by reading through the encoder of that software and its readme. 31 | 32 | 33 | ##### Target application: Writing a rUDP Protocol 34 | 35 | * If you're writing your own reliable UDP protocol, this can save you a bunch 36 | of time for the trickier parts of the code to write. 37 | 38 | It can also generate selective acknowledgements and retransmitted data to be 39 | useful as the core engine of a Hybrid ARQ transport protocol, and it exposes 40 | its custom memory allocator to help implement outgoing data queues. 41 | 42 | 43 | ##### Example Usage: 44 | 45 | Example codec usage with error checking omitted: 46 | 47 | ~~~ 48 | SiameseEncoder encoder = siamese_encoder_create(); 49 | SiameseDecoder decoder = siamese_decoder_create(); 50 | 51 | // For each original datagram: 52 | 53 | SiameseOriginalPacket original; 54 | original.Data = buffer; 55 | original.DataBytes = bytes; 56 | 57 | siamese_encoder_add(encoder, &original); 58 | siamese_decoder_add_original(decoder, &original); 59 | 60 | // For each recovery datagram: 61 | 62 | SiameseRecoveryPacket recovery; 63 | siamese_encode(encoder, &recovery); 64 | 65 | siamese_decoder_add_recovery(decoder, &recovery); 66 | 67 | if (0 == siamese_decoder_is_ready(decoder)) 68 | { 69 | SiameseOriginalPacket* recovered = nullptr; 70 | unsigned recoveredCount = 0; 71 | siamese_decode(decoder, &recovered, &recoveredCount); 72 | 73 | // Process recovered data here. 74 | } 75 | ~~~ 76 | 77 | There are more detailed examples in [unit_test.cpp](https://github.com/catid/siamese/blob/master/tests/unit_test.cpp). 78 | 79 | 80 | #### Comparisons 81 | 82 | Siamese is fairly different from the other erasure coding software I've released. 83 | 84 | All the other ones are block codes, meaning they take a block of input at a time. Summary: 85 | 86 | [cm256](https://github.com/catid/cm256) : GF(256) Cauchy Reed-Solomon block code. Limited to 255 inputs or outputs. Input data cannot change between outputs. Recovery never fails. 87 | 88 | [longhair](https://github.com/catid/longhair) : Binary(XOR-only) Cauchy Reed-Solomon block code. Limited to 255 inputs or outputs. Inputs must be a multiple of 8 bytes. Input data cannot change between outputs. Recovery never fails. 89 | 90 | [wirehair](https://github.com/catid/wirehair) : Complex LDPC+HDPC block code. Up to 64,000 inputs in a block. Unlimited outputs. Decoder takes about the same time regardless of number of losses, implying that one lost packet takes a long time to recover. Input data cannot change between outputs. Recovery can fail about 1% of the time. 91 | 92 | Siamese : Artifically limited to 16,000 inputs. Artificially limited to 256 outputs. Inputs *can* change between outputs. Decoder takes time proportional to the number of losses as O(N^2). 93 | 94 | For small loss count it's faster than Wirehair, and past a certain point (~10% loss) encode+decode time is longer than Wirehair. 95 | 96 | At lower data rates, Siamese uses a Cauchy Reed-Solomon code: Recovery never fails. 97 | At higher data rates, Siamese switches to a new structured linear convolutional code: It fails to recover about 1% of the time. 98 | 99 | Many of the parameters of the code are tunable to trade between performance and recovery rate. 100 | 101 | 102 | #### How Siamese works 103 | 104 | The library uses Siamese Codes for a structured convolutional matrix. This matrix has a fast matrix-vector product involving mostly XOR operations. This allows Siamese Codes to encode and decode much faster than other convolutional codes built on Cauchy or Vandermonde matrices. Let's call this the Siamese Matrix Structure or something similar. 105 | 106 | To produce an output packet, some preprocessing is performed. 107 | 108 | The input data is first split into 8 "lanes" where every 8th symbol {e.g. 0, 8, 16, 24, ...} is summed together. The second "lane" starts from input symbol 1 and contains every 8th symbol after that {e.g. 1, 9, 17, 25, ...}. 109 | 110 | For each "lane" there are three running "sums": 111 | 112 | Sum 0: Simple XOR between all inputs in that lane. 113 | Sum 1: Each input is multiplied by a coefficient provided by GetColumnValue, and then XORed into the sum. 114 | Sum 2: Each input is multiplied by the same coefficient squared, and then XORed into the sum. 115 | This means there are 24 running sums, each with symbol_bytes bytes of data. 116 | 117 | When an output is being produced (encoded), two running sums are formed temporarily. Both are generated through the same process, and the result of one sum is multiplied by a row coefficient produced by the GetRowValue function and added to the other sum to produce the output. 118 | 119 | To produce each of the two sums, a formula is followed. For each lane, the GetRowOpcode function returns which sums should be used. Sums 0, 1, and 2 are incorporated in based on the function output. And then 1/16 of the input data are selected at random and XORed into each sum. 120 | 121 | The Siamese codec and the Fecal decoder both will compute lane sums only when they are needed. Since some of the 24 sums (about 50%) are unneeded, the number of operations will vary for each row. 122 | 123 | The final random XOR is similar to an LDPC code and allows the recovery properties of the code to perform well on a larger scale above about 32 input symbols. The GF(2^^8) multiplies dominate the recovery properties for smaller losses and input symbols. The specific code used was selected by experimenting with different parameters until a desired failure rate was achieved with good performance characteristics. 124 | 125 | As a result the Siamese Codes mainly use XORs. So it can run a lot faster than straight GF(2^^8) multiply-add operations. Since they are still Convolutional Codes, the Siamese Codes also lend themselves to streaming use case. 126 | 127 | When AVX2 and SSSE3 are unavailable, Siamese takes 4x longer to decode and 2.6x longer to encode. Encoding requires a lot more simple XOR ops so it is still pretty fast. Decoding is usually really quick because average loss rates are low, but when needed it requires a lot more GF multiplies requiring table lookups which is slower. 128 | 129 | 130 | #### Credits 131 | 132 | Software by Christopher A. Taylor mrcatid@gmail.com 133 | 134 | Please reach out if you need support or would like to collaborate on a project. 135 | -------------------------------------------------------------------------------- /SiameseCommon.cpp: -------------------------------------------------------------------------------- 1 | /** \file 2 | \brief Siamese FEC Implementation: Codec Common Definitions 3 | \copyright Copyright (c) 2017 Christopher A. Taylor. All rights reserved. 4 | 5 | Redistribution and use in source and binary forms, with or without 6 | modification, are permitted provided that the following conditions are met: 7 | 8 | * Redistributions of source code must retain the above copyright notice, 9 | this list of conditions and the following disclaimer. 10 | * Redistributions in binary form must reproduce the above copyright notice, 11 | this list of conditions and the following disclaimer in the documentation 12 | and/or other materials provided with the distribution. 13 | * Neither the name of Siamese nor the names of its contributors may be 14 | used to endorse or promote products derived from this software without 15 | specific prior written permission. 16 | 17 | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" 18 | AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 19 | IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 20 | ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE 21 | LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR 22 | CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF 23 | SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS 24 | INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN 25 | CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) 26 | ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE 27 | POSSIBILITY OF SUCH DAMAGE. 28 | */ 29 | 30 | #include "SiameseCommon.h" 31 | #include "SiameseSerializers.h" 32 | 33 | namespace siamese { 34 | 35 | 36 | //------------------------------------------------------------------------------ 37 | // GrowingAlignedByteMatrix 38 | 39 | void GrowingAlignedByteMatrix::Free(pktalloc::Allocator* allocator) 40 | { 41 | SIAMESE_DEBUG_ASSERT(allocator); 42 | if (Data) 43 | { 44 | allocator->Free(Data); 45 | Data = nullptr; 46 | AllocatedRows = 0; 47 | AllocatedColumns = 0; 48 | } 49 | } 50 | 51 | bool GrowingAlignedByteMatrix::Initialize(pktalloc::Allocator* allocator, unsigned rows, unsigned columns) 52 | { 53 | Rows = rows; 54 | Columns = columns; 55 | AllocatedRows = rows + kExtraRows; 56 | AllocatedColumns = pktalloc::NextAlignedOffset(columns + kMinExtraColumns); 57 | 58 | Data = allocator->Reallocate(Data, AllocatedRows * AllocatedColumns, pktalloc::Realloc::Uninitialized); 59 | 60 | return Data != nullptr; 61 | } 62 | 63 | bool GrowingAlignedByteMatrix::Resize(pktalloc::Allocator* allocator, unsigned rows, unsigned columns) 64 | { 65 | SIAMESE_DEBUG_ASSERT(allocator && rows > 0 && columns > 0 && columns <= kColumnPeriod); 66 | if (rows <= AllocatedRows && columns <= AllocatedColumns) 67 | { 68 | Rows = rows; 69 | Columns = columns; 70 | return true; 71 | } 72 | 73 | const unsigned allocatedRows = rows + kExtraRows; 74 | const unsigned allocatedColumns = pktalloc::NextAlignedOffset(columns + kMinExtraColumns); 75 | 76 | uint8_t* buffer = allocator->Allocate(allocatedRows * allocatedColumns); 77 | if (!buffer) 78 | { 79 | Free(allocator); 80 | return false; 81 | } 82 | 83 | // If we already allocated a buffer: 84 | if (Data) 85 | { 86 | uint8_t* oldBuffer = Data; 87 | const unsigned oldColumns = Columns; 88 | 89 | if (oldColumns > 0) 90 | { 91 | // Maintain old data 92 | const unsigned oldRows = Rows; 93 | const unsigned oldStride = AllocatedColumns; 94 | uint8_t* destRow = buffer; 95 | uint8_t* srcRow = oldBuffer; 96 | 97 | unsigned copyCount = oldColumns; 98 | if (copyCount > columns) 99 | { 100 | SIAMESE_DEBUG_BREAK(); // Should never happen 101 | copyCount = columns; 102 | } 103 | 104 | for (unsigned i = 0; i < oldRows; ++i, destRow += allocatedColumns, srcRow += oldStride) 105 | memcpy(destRow, srcRow, copyCount); 106 | } 107 | 108 | allocator->Free(oldBuffer); 109 | } 110 | 111 | AllocatedRows = allocatedRows; 112 | AllocatedColumns = allocatedColumns; 113 | Rows = rows; 114 | Columns = columns; 115 | Data = buffer; 116 | return true; 117 | } 118 | 119 | 120 | //------------------------------------------------------------------------------ 121 | // OriginalPacket 122 | 123 | unsigned OriginalPacket::Initialize(pktalloc::Allocator* allocator, const SiameseOriginalPacket& packet) 124 | { 125 | SIAMESE_DEBUG_ASSERT(allocator && packet.Data && packet.DataBytes > 0 && packet.PacketNum < kColumnPeriod); 126 | 127 | // Allocate space for the packet 128 | const unsigned bufferSize = kMaxPacketLengthFieldBytes + packet.DataBytes; 129 | if (!Buffer.Initialize(allocator, bufferSize)) 130 | return 0; 131 | 132 | // Serialize the packet length into the front using a compressed format 133 | HeaderBytes = SerializeHeader_PacketLength(packet.DataBytes, Buffer.Data); 134 | SIAMESE_DEBUG_ASSERT(HeaderBytes <= kMaxPacketLengthFieldBytes); 135 | 136 | // Copy packet data after the length 137 | memcpy(Buffer.Data + HeaderBytes, packet.Data, packet.DataBytes); 138 | 139 | Buffer.Bytes = HeaderBytes + packet.DataBytes; 140 | 141 | Column = packet.PacketNum; 142 | 143 | return HeaderBytes; 144 | } 145 | 146 | 147 | } // namespace siamese 148 | -------------------------------------------------------------------------------- /SiameseCommon.h: -------------------------------------------------------------------------------- 1 | /** \file 2 | \brief Siamese FEC Implementation: Codec Common Definitions 3 | \copyright Copyright (c) 2017 Christopher A. Taylor. All rights reserved. 4 | 5 | Redistribution and use in source and binary forms, with or without 6 | modification, are permitted provided that the following conditions are met: 7 | 8 | * Redistributions of source code must retain the above copyright notice, 9 | this list of conditions and the following disclaimer. 10 | * Redistributions in binary form must reproduce the above copyright notice, 11 | this list of conditions and the following disclaimer in the documentation 12 | and/or other materials provided with the distribution. 13 | * Neither the name of Siamese nor the names of its contributors may be 14 | used to endorse or promote products derived from this software without 15 | specific prior written permission. 16 | 17 | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" 18 | AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 19 | IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 20 | ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE 21 | LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR 22 | CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF 23 | SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS 24 | INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN 25 | CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) 26 | ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE 27 | POSSIBILITY OF SUCH DAMAGE. 28 | */ 29 | 30 | #pragma once 31 | 32 | /** 33 | This module provides core tools and constants used by the codec: 34 | 35 | + Parameters of the Siamese and Cauchy matrix structures 36 | + Growing buffer/matrix structures 37 | + OriginalPacket structure 38 | + RecoveryMetadata structure 39 | */ 40 | 41 | #include "siamese.h" 42 | #include "SiameseTools.h" 43 | 44 | #include "PacketAllocator.h" 45 | #include "Logger.h" 46 | 47 | #include "gf256.h" 48 | static_assert(PKTALLOC_ALIGN_BYTES == GF256_ALIGN_BYTES, "headers are fighting"); 49 | 50 | #include 51 | #include 52 | #include 53 | #include 54 | 55 | /* 56 | Ideas: 57 | + Steal bits from length field for recovery metadata 58 | + Packet Ids should be relative to the last acknowledged packet to save bytes 59 | */ 60 | 61 | namespace siamese { 62 | 63 | 64 | //------------------------------------------------------------------------------ 65 | // Compiler Flags 66 | 67 | /// Mix in Cauchy and parity rows to improve recovery rate and speed if possible 68 | #define SIAMESE_ENABLE_CAUCHY 69 | 70 | /// Verbose diagnostic output 71 | //#define SIAMESE_DECODER_DUMP_SOLVER_PERF 72 | //#define SIAMESE_DECODER_DUMP_VERBOSE 73 | //#define SIAMESE_ENCODER_DUMP_VERBOSE 74 | 75 | 76 | //------------------------------------------------------------------------------ 77 | // Code Parameters 78 | 79 | /// Maximum recovery loss count to avoid causing huge delays or hitting limits 80 | static const unsigned kMaximumLossRecoveryCount = 255; 81 | 82 | /// Number of values 3..255 that we cycle through 83 | static const unsigned kColumnValuePeriod = 253; 84 | 85 | /// Number of values 1..255 that we cycle through 86 | static const unsigned kRowValuePeriod = 255; 87 | 88 | 89 | SIAMESE_FORCE_INLINE uint8_t GetColumnValue(unsigned column) 90 | { 91 | // Note: This LCG visits each value exactly once 92 | return (uint8_t)(3 + (column * 199) % kColumnValuePeriod); 93 | } 94 | 95 | SIAMESE_FORCE_INLINE uint8_t GetRowValue(unsigned row) 96 | { 97 | return (uint8_t)(1 + (row + 1) % kRowValuePeriod); 98 | } 99 | 100 | 101 | /// Choose the row/column periods to be a power-of-two to simplify wrap-around calculations 102 | static const unsigned kColumnPeriod = 0x400000; 103 | static const unsigned kRowPeriod = kRowValuePeriod; 104 | 105 | /// Returns true if the provided column difference is negative 106 | SIAMESE_FORCE_INLINE bool IsColumnDeltaNegative(unsigned columnDelta) 107 | { 108 | return (columnDelta >= kColumnPeriod / 2); 109 | } 110 | 111 | /// Returns delta (column_a - column_b) between two columns 112 | SIAMESE_FORCE_INLINE unsigned SubtractColumns(unsigned column_a, unsigned column_b) 113 | { 114 | return (column_a - column_b) % kColumnPeriod; 115 | } 116 | 117 | /// Returns sum of two columns as a column number 118 | SIAMESE_FORCE_INLINE unsigned AddColumns(unsigned column_a, unsigned column_b) 119 | { 120 | return (column_a + column_b) % kColumnPeriod; 121 | } 122 | 123 | /// Increments a column number by 1 and returns the incremented value 124 | SIAMESE_FORCE_INLINE unsigned IncrementColumn1(unsigned column) 125 | { 126 | return AddColumns(column, 1); 127 | } 128 | 129 | /// Number of parallel lanes to run 130 | /// Lane#(Column) = Column % kColumnLaneCount 131 | static const unsigned kColumnLaneCount = 8; 132 | 133 | /// Number of running sums of original data 134 | /// Note: This cannot be tuned without making code changes 135 | /// Sum 0 = Parity XOR of all input data 136 | /// Sum 1 = Product #1 sum XOR of all input data times its GetColumnValue() 137 | /// Sum 2 = Product #2 sum XOR of all input data times its GetColumnValue() squared 138 | static const unsigned kColumnSumCount = 3; 139 | 140 | /// Rate at which we add random pairs of data 141 | static const unsigned kPairAddRate = 16; 142 | 143 | /// Keep this number of columns in each subwindow. 144 | /// Data is eliminated from the front of the window at intervals 145 | /// of this size in order to reduce the cost of elimination 146 | static const unsigned kSubwindowSize = kColumnLaneCount * 8; 147 | 148 | /// Thomas Wang's 32-bit -> 32-bit integer hash function 149 | /// http://burtleburtle.net/bob/hash/integer.html 150 | SIAMESE_FORCE_INLINE uint32_t Int32Hash(uint32_t key) 151 | { 152 | key += ~(key << 15); 153 | key ^= (key >> 10); 154 | key += (key << 3); 155 | key ^= (key >> 6); 156 | key += ~(key << 11); 157 | key ^= (key >> 16); 158 | return key; 159 | } 160 | 161 | /// Calculate operation code for the given row and lane 162 | SIAMESE_FORCE_INLINE unsigned GetRowOpcode(unsigned lane, unsigned row) 163 | { 164 | SIAMESE_DEBUG_ASSERT(lane < kColumnLaneCount && row < kRowPeriod); 165 | static const uint32_t kSumMask = (1 << (kColumnSumCount * 2)) - 1; 166 | static const uint32_t kZeroValue = (1 << ((kColumnSumCount - 1) * 2)); 167 | 168 | // This offset tunes the quality of the upper left of the generated matrix, 169 | // which is encountered in practice for the first block of input data 170 | static const unsigned kArbitraryOffset = 3; 171 | 172 | const uint32_t opcode = Int32Hash(lane + (row + kArbitraryOffset) * kColumnLaneCount) & kSumMask; 173 | return (opcode == 0) ? kZeroValue : (unsigned)opcode; 174 | } 175 | 176 | 177 | //------------------------------------------------------------------------------ 178 | // MDS Erasure Codes using Cauchy Matrix 179 | 180 | /** 181 | For short codes the Siamese matrix multiplication algorithm is slower than 182 | Cauchy matrix multiplication, so when the number of columns is small we use 183 | the faster matrix structure instead, which also has perfect recovery rate. 184 | 185 | But the MDS Cauchy matrix is limited: It can only be used for around 200 186 | input packets in practice. Furthermore it starts running slower around 64 187 | input packets so after that point we switch over to the full Siamese codec. 188 | */ 189 | #ifdef SIAMESE_ENABLE_CAUCHY 190 | 191 | /// At/below this number of packets, we use a Cauchy coefficients for better 192 | /// recovery properties and faster encoding. 193 | /// For example, a window of 64 packets will use Cauchy instead of Siamese sums. 194 | #define SIAMESE_CAUCHY_THRESHOLD 64 /**< Better under 6 recovery than Siamese */ 195 | 196 | /// If the window shrinks at/below this number of packets, we switch back to 197 | /// using Cauchy coefficients for matrix rows. This should be less than the 198 | /// SIAMESE_CAUCHY_THRESHOLD to allow some hysteresis (fuzzy-logic). 199 | #define SIAMESE_SUM_RESET_THRESHOLD 32 /**< Reset sums if window shrinks */ 200 | 201 | static const unsigned kCauchyMaxColumns = SIAMESE_CAUCHY_THRESHOLD; 202 | static const unsigned kCauchyMaxRows = 256 - kCauchyMaxColumns; 203 | 204 | static_assert(kCauchyMaxColumns <= 128, "too high"); 205 | 206 | 207 | /// Cauchy matrix element definitions for short codes 208 | /// CauchyElement(i, j) = 1 / (X(i) - Y(j)) in GF(2^^8) 209 | /// X(i) = i + kCauchyMaxColumns 210 | /// Y(j) = j 211 | /// Preconditions: row < kCauchyMaxRows, column < kCauchyMaxColumns 212 | SIAMESE_FORCE_INLINE uint8_t CauchyElement(unsigned row, unsigned column) 213 | { 214 | SIAMESE_DEBUG_ASSERT(row < kCauchyMaxRows && column < kCauchyMaxColumns); 215 | const uint8_t y_j = (uint8_t)column; 216 | const uint8_t x_i = (uint8_t)(row + kCauchyMaxColumns); 217 | return gf256_inv(x_i ^ y_j); 218 | } 219 | 220 | #endif // SIAMESE_ENABLE_CAUCHY 221 | 222 | 223 | //------------------------------------------------------------------------------ 224 | // GrowingAlignedDataBuffer 225 | 226 | /// A buffer of data that can grow as needed and has good memory alignment for 227 | /// using SIMD operations on it. 228 | struct GrowingAlignedDataBuffer 229 | { 230 | /// Buffer data 231 | uint8_t* Data = nullptr; 232 | 233 | /// Number of bytes of valid data 234 | unsigned Bytes = 0; 235 | 236 | 237 | /// Growing mantaining existing data in the buffer 238 | /// Newly grown buffer space will be initialized to zeros 239 | bool GrowZeroPadded(pktalloc::Allocator* allocator, unsigned bytes) 240 | { 241 | SIAMESE_DEBUG_ASSERT(allocator && bytes > 0); 242 | if (!Data || bytes > Bytes) 243 | { 244 | Data = allocator->Reallocate(Data, bytes, pktalloc::Realloc::CopyExisting); 245 | if (!Data) 246 | { 247 | Bytes = 0; 248 | return false; 249 | } 250 | 251 | memset(Data + Bytes, 0, bytes - Bytes); 252 | Bytes = bytes; 253 | } 254 | return true; 255 | } 256 | 257 | /// Growing *dropping* existing data in the buffer 258 | /// Newly grown buffer space will *not* be initialized to zeros 259 | bool Initialize(pktalloc::Allocator* allocator, unsigned bytes) 260 | { 261 | SIAMESE_DEBUG_ASSERT(allocator && bytes > 0); 262 | Data = allocator->Reallocate(Data, bytes, pktalloc::Realloc::Uninitialized); 263 | if (!Data) 264 | { 265 | Bytes = 0; 266 | return false; 267 | } 268 | Bytes = bytes; 269 | return true; 270 | } 271 | 272 | /// Free allocated memory 273 | void Free(pktalloc::Allocator* allocator) 274 | { 275 | SIAMESE_DEBUG_ASSERT(allocator); 276 | if (Data) 277 | { 278 | allocator->Free(Data); 279 | Data = nullptr; 280 | Bytes = 0; 281 | } 282 | } 283 | }; 284 | 285 | 286 | //------------------------------------------------------------------------------ 287 | // GrowingAlignedByteMatrix 288 | 289 | /// This is a matrix of bytes where the elements are stored in row-first order 290 | /// and the first byte element of each row is aligned to cache-line boundaries. 291 | /// Furthermore the matrix can grow in rows or columns, keeping existing data. 292 | struct GrowingAlignedByteMatrix 293 | { 294 | /// Buffer data 295 | uint8_t* Data = nullptr; 296 | 297 | /// Used rows, columns 298 | unsigned Rows = 0; 299 | unsigned Columns = 0; 300 | 301 | /// Allocate a few extra rows, columns whenenver we grow the matrix 302 | /// This is tuned for the expected maximum recovery failure rate 303 | static const unsigned kExtraRows = 4; 304 | static const unsigned kMinExtraColumns = 4; 305 | 306 | /// Allocated rows, columns 307 | unsigned AllocatedRows = 0; 308 | unsigned AllocatedColumns = 0; 309 | 310 | 311 | /// Initialize matrix to the given size 312 | /// New elements have undefined initial state 313 | bool Initialize(pktalloc::Allocator* allocator, unsigned rows, unsigned columns); 314 | 315 | /// Growing mantaining existing data in the buffer 316 | /// New elements have undefined initial state 317 | bool Resize(pktalloc::Allocator* allocator, unsigned rows, unsigned columns); 318 | 319 | /// Reset to initial empty state 320 | SIAMESE_FORCE_INLINE void Clear() 321 | { 322 | Rows = 0; 323 | Columns = 0; 324 | } 325 | 326 | /// Getter 327 | SIAMESE_FORCE_INLINE uint8_t Get(unsigned row, unsigned column) 328 | { 329 | SIAMESE_DEBUG_ASSERT(Data && row < Rows && column < Columns); 330 | return Data[row * AllocatedColumns + column]; 331 | } 332 | 333 | /// Free allocated memory 334 | void Free(pktalloc::Allocator* allocator); 335 | }; 336 | 337 | 338 | //------------------------------------------------------------------------------ 339 | // OriginalPacket 340 | 341 | /// Original packet 342 | struct OriginalPacket 343 | { 344 | /// Original packet data, prefixed with length field 345 | GrowingAlignedDataBuffer Buffer; 346 | 347 | /// Keep track of the column index for this packet 348 | unsigned Column = 0; 349 | 350 | /// Keep track of the number of bytes for header on the packet data 351 | unsigned HeaderBytes = 0; 352 | 353 | 354 | /// Write data to buffer with length prefix and initialize other members 355 | /// Returns the number of bytes overhead, or 0 on out-of-memory error 356 | unsigned Initialize(pktalloc::Allocator* allocator, const SiameseOriginalPacket& packet); 357 | }; 358 | 359 | 360 | //------------------------------------------------------------------------------ 361 | // Recovery Metadata 362 | 363 | /// Metadata header attached to each recovery packet 364 | struct RecoveryMetadata 365 | { 366 | /// Recovery packet identifer 0..SIAMESE_RECOVERY_NUM_MAX 367 | unsigned Row; ///< 1 byte 368 | 369 | /// A value between 0..SIAMESE_PACKET_NUM_MAX 370 | unsigned ColumnStart; ///< up to 3 bytes 371 | 372 | /// Number of packets in the sum set 1..SIAMESE_MAX_PACKETS 373 | /// These start from ColumnStart 374 | unsigned SumCount; ///< up to 2 bytes 375 | 376 | /// Number of packets in the LDPC set 1..SIAMESE_MAX_PACKETS 377 | /// These are on the right side and overlapping with the sum set 378 | unsigned LDPCCount; ///< up to 2 bytes 379 | 380 | /** 381 | Visualization of the relationship between ColumnStart, 382 | SumCount, and LDPCCount: 383 | 384 | | - - LDPC Count - - | 385 | | - - - - - - Sum Count - - - - - - - | 386 | ^ 387 | ColumnStart 388 | */ 389 | }; 390 | 391 | 392 | } // namespace siamese 393 | -------------------------------------------------------------------------------- /SiameseEncoder.h: -------------------------------------------------------------------------------- 1 | /** \file 2 | \brief Siamese FEC Implementation: Encoder 3 | \copyright Copyright (c) 2017 Christopher A. Taylor. All rights reserved. 4 | 5 | Redistribution and use in source and binary forms, with or without 6 | modification, are permitted provided that the following conditions are met: 7 | 8 | * Redistributions of source code must retain the above copyright notice, 9 | this list of conditions and the following disclaimer. 10 | * Redistributions in binary form must reproduce the above copyright notice, 11 | this list of conditions and the following disclaimer in the documentation 12 | and/or other materials provided with the distribution. 13 | * Neither the name of Siamese nor the names of its contributors may be 14 | used to endorse or promote products derived from this software without 15 | specific prior written permission. 16 | 17 | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" 18 | AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 19 | IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 20 | ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE 21 | LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR 22 | CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF 23 | SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS 24 | INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN 25 | CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) 26 | ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE 27 | POSSIBILITY OF SUCH DAMAGE. 28 | */ 29 | 30 | #pragma once 31 | 32 | /** 33 | Encoder 34 | 35 | The encoder keeps track of packets that have not yet been acknowledged by 36 | the decoder, and when asked to encode it will select between a Cauchy matrix 37 | or a more complicated Siamese matrix row. 38 | */ 39 | 40 | #include "SiameseCommon.h" 41 | 42 | namespace siamese { 43 | 44 | 45 | /** 46 | Terminology: 47 | 48 | + Packet Number = Number assigned to each original packet that is unique 49 | for a window of data being processed by the codec. It wraps around to 0. 50 | 51 | + Column Number = Same as Packet Number. 52 | 53 | + Window Element = A packet in the Subwindows array. 0 is the first 54 | array position in the first subwindow, corresponding to ColumnStart. 55 | */ 56 | 57 | 58 | //------------------------------------------------------------------------------ 59 | // EncoderStats 60 | 61 | struct EncoderStats 62 | { 63 | /// SiameseEncoderStats 64 | uint64_t Counts[SiameseEncoderStats_Count]; 65 | 66 | EncoderStats(); 67 | }; 68 | 69 | 70 | //------------------------------------------------------------------------------ 71 | // EncoderColumnLane 72 | 73 | struct EncoderColumnLane 74 | { 75 | /// Next element to accumulate, once we get it from the application 76 | unsigned NextElement[kColumnSumCount]; 77 | 78 | /// Running sums. See kColumnSumCount definition 79 | GrowingAlignedDataBuffer Sum[kColumnSumCount]; 80 | 81 | /// Longest packet in this lane 82 | /// Note: I think it's a win to keep this per-lane because if the 83 | /// data size is highly variable we may reduce memory accesses 84 | unsigned LongestPacket = 0; 85 | }; 86 | 87 | 88 | //------------------------------------------------------------------------------ 89 | // EncoderSubwindow 90 | 91 | struct EncoderSubwindow 92 | { 93 | /// Original packets in this subwindow indexed by packet number 94 | std::array Originals; 95 | 96 | /// Timestamp at which we last sent the packet 97 | std::array LastSendMsec; 98 | }; 99 | 100 | 101 | //------------------------------------------------------------------------------ 102 | // EncoderPacketWindow 103 | 104 | struct EncoderPacketWindow 105 | { 106 | pktalloc::Allocator* TheAllocator = nullptr; 107 | EncoderStats* Stats = nullptr; 108 | 109 | /// Next column number to assign to a packet 110 | unsigned NextColumn = 0; 111 | 112 | /// Count of packets so far 113 | unsigned Count = 0; 114 | 115 | /// Start column of set 116 | /// Note: When Count == 0, this is undefined 117 | unsigned ColumnStart = 0; 118 | 119 | /// Longest packet 120 | /// Note: Undefined if count == 0 121 | unsigned LongestPacket = 0; 122 | 123 | /// Note: This is updated by RemoveUpTo() 124 | unsigned FirstUnremovedElement = 0; 125 | 126 | /// Sum element range [start...end) 127 | /// Note: End is the first element outside of the range 128 | unsigned SumStartElement = 0; 129 | unsigned SumEndElement = 0; 130 | unsigned SumColumnStart = 0; 131 | unsigned SumErasedCount = 0; 132 | 133 | /// Allocated Subwindows 134 | pktalloc::LightVector Subwindows; 135 | 136 | /// Running summations for each lane 137 | EncoderColumnLane Lanes[kColumnLaneCount]; 138 | 139 | /// Temporary workspace reused each time subwindows must be shifted 140 | pktalloc::LightVector SubwindowsShift; 141 | 142 | /// If input is invalid or we run out of memory, the encoder is disabled 143 | /// to prevent it from allowing exploits to run or cause crashes 144 | bool EmergencyDisabled = false; 145 | 146 | 147 | /// Ctor initializes elements to default values 148 | EncoderPacketWindow(); 149 | 150 | /// Convert a column to a window element 151 | SIAMESE_FORCE_INLINE unsigned ColumnToElement(unsigned column) const 152 | { 153 | return SubtractColumns(column, ColumnStart); 154 | } 155 | 156 | /// Validate that an element is within the window 157 | SIAMESE_FORCE_INLINE bool InvalidElement(unsigned element) const 158 | { 159 | return (element >= Count); 160 | } 161 | 162 | /// Convert a window element to a column 163 | SIAMESE_FORCE_INLINE unsigned ElementToColumn(unsigned element) const 164 | { 165 | return AddColumns(element, ColumnStart); 166 | } 167 | 168 | /// Get element from the window, indexed by window offset not column number 169 | /// Precondition: 0 <= element < Count 170 | SIAMESE_FORCE_INLINE OriginalPacket* GetWindowElement(unsigned windowElement) 171 | { 172 | SIAMESE_DEBUG_ASSERT(windowElement < Count); 173 | return &(Subwindows.GetRef(windowElement / kSubwindowSize)->Originals[windowElement % kSubwindowSize]); 174 | } 175 | 176 | /// Get element send timestamp from the window, indexed by window offset not column number 177 | /// Precondition: 0 <= element < Count 178 | SIAMESE_FORCE_INLINE uint32_t* GetWindowElementTimestampPtr(unsigned windowElement) 179 | { 180 | SIAMESE_DEBUG_ASSERT(windowElement < Count); 181 | return &(Subwindows.GetRef(windowElement / kSubwindowSize)->LastSendMsec[windowElement % kSubwindowSize]); 182 | } 183 | 184 | /// How many slots remain in the window? 185 | SIAMESE_FORCE_INLINE unsigned GetRemainingSlots() const 186 | { 187 | SIAMESE_DEBUG_ASSERT(SIAMESE_MAX_PACKETS >= Count); 188 | return SIAMESE_MAX_PACKETS - Count; 189 | } 190 | 191 | /// Append a packet to the end of the set 192 | SiameseResult Add(SiameseOriginalPacket& packet); 193 | 194 | /// Removes elements up to the given column 195 | void RemoveBefore(unsigned firstKeptColumn); 196 | 197 | /// Get next element at or after the given element that is in the given lane 198 | unsigned GetNextLaneElement(unsigned element, unsigned laneIndex) 199 | { 200 | SIAMESE_DEBUG_ASSERT(element < Count); 201 | SIAMESE_DEBUG_ASSERT(laneIndex < kColumnLaneCount); 202 | unsigned nextElement = element - (element % kColumnLaneCount) + laneIndex; 203 | if (nextElement < element) 204 | nextElement += kColumnLaneCount; 205 | SIAMESE_DEBUG_ASSERT(nextElement >= element); 206 | SIAMESE_DEBUG_ASSERT(nextElement % kColumnLaneCount == laneIndex); 207 | SIAMESE_DEBUG_ASSERT(nextElement < Count + kColumnLaneCount); 208 | return nextElement; 209 | } 210 | 211 | /// Reset lane sums from the given start element 212 | void ResetSums(unsigned elementStart); 213 | 214 | /// Get running sums for a lane 215 | const GrowingAlignedDataBuffer* GetSum(unsigned laneIndex, unsigned sumIndex, unsigned elementEnd); 216 | 217 | /// Returns the number of elements that have not been acknowledged yet 218 | unsigned GetUnacknowledgedCount() 219 | { 220 | SIAMESE_DEBUG_ASSERT(FirstUnremovedElement < Count || Count == 0); 221 | return Count - FirstUnremovedElement; 222 | } 223 | 224 | /// Start a new window from the given column 225 | void StartNewWindow(unsigned column); 226 | 227 | /// Clear the window 228 | void ClearWindow(); 229 | 230 | /// Precondition: FirstUsedElement >= kSubwindowSize 231 | void RemoveElements(); 232 | }; 233 | 234 | 235 | //------------------------------------------------------------------------------ 236 | // EncoderAcknowledgementState 237 | 238 | /// State related to the last received acknowledgement 239 | struct EncoderAcknowledgementState 240 | { 241 | pktalloc::Allocator* TheAllocator = nullptr; 242 | EncoderPacketWindow* TheWindow = nullptr; 243 | 244 | /// Loss range list raw data, copied from the acknowledgement 245 | uint8_t* Data = nullptr; 246 | 247 | /// Number of bytes used by the loss range data 248 | unsigned DataBytes = 0; 249 | 250 | /// Padding on the loss range data for speeding up decoding 251 | static const unsigned kPaddingBytes = 8; 252 | 253 | /// Next byte to process 254 | unsigned Offset = 0; 255 | 256 | /// Next column lost 257 | unsigned LossColumn = 0; 258 | 259 | /// Number of losses left in the current range 260 | unsigned LossCount = 0; 261 | 262 | /// Next column expected by receiver 263 | unsigned NextColumnExpected = 0; 264 | 265 | /// Next column to be used for RTO calculations 266 | unsigned NextRTOColumn = 0; 267 | 268 | /// If the oldest column is already found, then keep trying to send it 269 | bool FoundOldest = false; 270 | 271 | /// This is the oldest column to resend 272 | unsigned OldestColumn = 0; 273 | 274 | /// Initial Retransmit Timeout (RTO) in milliseconds 275 | static const unsigned kInitialRetransmitTimeoutMsec = 500; ///< milliseconds 276 | 277 | /// Retransmit Timeout (RTO) in milliseconds 278 | unsigned RetransmitTimeoutMsec = kInitialRetransmitTimeoutMsec; 279 | 280 | /// Maximum RTT value within a window 281 | WindowedMinMax< unsigned, WindowedMaxCompare > MaxWindowedRTT; 282 | 283 | 284 | /** 285 | OnAcknowledgementData() 286 | 287 | Handle acknowledgement data from decoder. 288 | 289 | This resets the NACK list iterator to the top, sets NextColumnExpected 290 | and moves NextQuickNACKColumn forward if needed. 291 | The FoundOldest state will be reset, forcing a re-scan of the NACK list. 292 | 293 | Returns true if the data was valid and state was updated. 294 | Return false if the data was invalid, leaving state unchanged. 295 | */ 296 | bool OnAcknowledgementData(const uint8_t* data, unsigned bytes); 297 | 298 | /// Returns true if the column iterator is pointing at the first column 299 | SIAMESE_FORCE_INLINE bool IsIteratorAtFront() const 300 | { 301 | return LossColumn == NextColumnExpected; 302 | } 303 | 304 | /// Returns true if there are any negative acknowledgements 305 | SIAMESE_FORCE_INLINE bool HasNegativeAcknowledgements() const 306 | { 307 | return DataBytes > 0; 308 | } 309 | 310 | /// Get next loss column. 311 | /// Returns false if no more columns to read. Call RestartLossIterator() 312 | /// to restart the iteration when it returns false. 313 | bool GetNextLossColumn(unsigned& columnOut); 314 | 315 | /// Reset the loss iterator to the start so we read through them all again 316 | void RestartLossIterator(); 317 | 318 | /// Clear the ack data 319 | void Clear(); 320 | 321 | protected: 322 | /// Decode the next NACK range 323 | bool DecodeNextRange(); 324 | 325 | /// Update RTO based on latest NACK ranges 326 | void UpdateRTO(); 327 | }; 328 | 329 | 330 | //------------------------------------------------------------------------------ 331 | // Encoder 332 | 333 | /// Threshold number of elements before removing data 334 | static const unsigned kEncoderRemoveThreshold = 2 * kSubwindowSize; 335 | static_assert(kEncoderRemoveThreshold % kSubwindowSize == 0, "It removes on window boundaries"); 336 | 337 | class Encoder 338 | { 339 | public: 340 | Encoder(); 341 | 342 | SIAMESE_FORCE_INLINE unsigned GetRemainingSlots() const 343 | { 344 | return Window.GetRemainingSlots(); 345 | } 346 | 347 | /// Add an original data packet to the encoder 348 | SIAMESE_FORCE_INLINE SiameseResult Add(SiameseOriginalPacket& packet) 349 | { 350 | return Window.Add(packet); 351 | } 352 | 353 | /// Remove original data packet up to the given column 354 | SIAMESE_FORCE_INLINE void RemoveBefore(unsigned firstKeptColumn) 355 | { 356 | Window.RemoveBefore(firstKeptColumn); 357 | } 358 | 359 | /// Process an acknowledgement from the decoder 360 | SiameseResult Acknowledge( 361 | const uint8_t* data, 362 | unsigned bytes, 363 | unsigned& nextExpectedPacketNumOut); 364 | 365 | /// Retransmit an original packet in response to a NACK 366 | SiameseResult Retransmit(SiameseOriginalPacket& originalOut); 367 | 368 | /// Generate the next recovery packet for the data 369 | SiameseResult Encode(SiameseRecoveryPacket& recoveryOut); 370 | 371 | /// Get a packet in the set 372 | SiameseResult Get(SiameseOriginalPacket& packet); 373 | 374 | /// Get statistics 375 | SiameseResult GetStatistics(uint64_t* statsOut, unsigned statsCount); 376 | 377 | protected: 378 | /// When the allocator goes out of scope all our buffer allocations are freed 379 | pktalloc::Allocator TheAllocator; 380 | 381 | /// Collected statistics 382 | EncoderStats Stats; 383 | 384 | /// Set of encoded packets in the sliding window 385 | EncoderPacketWindow Window; 386 | 387 | /// Acknowledgement state 388 | EncoderAcknowledgementState Ack; 389 | 390 | /// Keeps a copy of the last recovery packet to speed up generating the next one 391 | GrowingAlignedDataBuffer RecoveryPacket; 392 | 393 | /// Next row to generate for Siamese rows 394 | unsigned NextRow = 0; 395 | 396 | /// Next start column that can be all ones 397 | unsigned NextParityColumn = 0; 398 | 399 | #ifdef SIAMESE_ENABLE_CAUCHY 400 | /// Next row to generate for Cauchy rows 401 | unsigned NextCauchyRow = 0; 402 | #endif // SIAMESE_ENABLE_CAUCHY 403 | 404 | 405 | /// Normal case of generating recovery packet 406 | void AddDenseColumns(unsigned row, uint8_t* productWorkspace); 407 | void AddLightColumns(unsigned row, uint8_t* productWorkspace); 408 | 409 | /// Generate output for the case of a single input packet 410 | SiameseResult GenerateSinglePacket(SiameseRecoveryPacket& packet); 411 | 412 | #ifdef SIAMESE_ENABLE_CAUCHY 413 | /// Generate output for the case of a small number of input packets 414 | SiameseResult GenerateCauchyPacket(SiameseRecoveryPacket& packet); 415 | #endif // SIAMESE_ENABLE_CAUCHY 416 | 417 | /// Attempt to retransmit the given original data 418 | SiameseResult AttemptRetransmit( 419 | OriginalPacket* original, 420 | SiameseOriginalPacket& originalOut); 421 | }; 422 | 423 | 424 | } // namespace siamese 425 | -------------------------------------------------------------------------------- /SiameseTools.cpp: -------------------------------------------------------------------------------- 1 | /** \file 2 | \brief Siamese FEC Implementation: Tools 3 | \copyright Copyright (c) 2017 Christopher A. Taylor. All rights reserved. 4 | 5 | Redistribution and use in source and binary forms, with or without 6 | modification, are permitted provided that the following conditions are met: 7 | 8 | * Redistributions of source code must retain the above copyright notice, 9 | this list of conditions and the following disclaimer. 10 | * Redistributions in binary form must reproduce the above copyright notice, 11 | this list of conditions and the following disclaimer in the documentation 12 | and/or other materials provided with the distribution. 13 | * Neither the name of Siamese nor the names of its contributors may be 14 | used to endorse or promote products derived from this software without 15 | specific prior written permission. 16 | 17 | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" 18 | AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 19 | IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 20 | ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE 21 | LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR 22 | CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF 23 | SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS 24 | INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN 25 | CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) 26 | ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE 27 | POSSIBILITY OF SUCH DAMAGE. 28 | */ 29 | 30 | #include "SiameseTools.h" 31 | 32 | #ifdef _WIN32 33 | #ifndef NOMINMAX 34 | #define NOMINMAX 35 | #endif 36 | #include 37 | #elif __MACH__ 38 | #include 39 | #include 40 | #include 41 | 42 | extern mach_port_t clock_port; 43 | #else 44 | #include 45 | #include 46 | #endif 47 | 48 | namespace siamese { 49 | 50 | 51 | //------------------------------------------------------------------------------ 52 | // Timing 53 | 54 | #ifdef _WIN32 55 | // Precomputed frequency inverse 56 | static double PerfFrequencyInverseUsec = 0.; 57 | static double PerfFrequencyInverseMsec = 0.; 58 | 59 | static void InitPerfFrequencyInverse() 60 | { 61 | LARGE_INTEGER freq = {}; 62 | if (!::QueryPerformanceFrequency(&freq) || freq.QuadPart == 0) 63 | return; 64 | const double invFreq = 1. / (double)freq.QuadPart; 65 | PerfFrequencyInverseUsec = 1000000. * invFreq; 66 | PerfFrequencyInverseMsec = 1000. * invFreq; 67 | SIAMESE_DEBUG_ASSERT(PerfFrequencyInverseUsec > 0.); 68 | SIAMESE_DEBUG_ASSERT(PerfFrequencyInverseMsec > 0.); 69 | } 70 | #elif __MACH__ 71 | static bool m_clock_serv_init = false; 72 | static clock_serv_t m_clock_serv = 0; 73 | 74 | static void InitClockServ() 75 | { 76 | m_clock_serv_init = true; 77 | host_get_clock_service(mach_host_self(), SYSTEM_CLOCK, &m_clock_serv); 78 | } 79 | #endif // _WIN32 80 | 81 | uint64_t GetTimeUsec() 82 | { 83 | #ifdef _WIN32 84 | LARGE_INTEGER timeStamp = {}; 85 | if (!::QueryPerformanceCounter(&timeStamp)) 86 | return 0; 87 | if (PerfFrequencyInverseUsec == 0.) 88 | InitPerfFrequencyInverse(); 89 | return (uint64_t)(PerfFrequencyInverseUsec * timeStamp.QuadPart); 90 | #elif __MACH__ 91 | if (!m_clock_serv_init) 92 | InitClockServ(); 93 | 94 | mach_timespec_t tv; 95 | clock_get_time(m_clock_serv, &tv); 96 | 97 | return 1000000 * tv.tv_sec + tv.tv_nsec / 1000; 98 | #else 99 | struct timeval tv; 100 | gettimeofday(&tv, nullptr); 101 | return 1000000 * tv.tv_sec + tv.tv_usec; 102 | #endif 103 | } 104 | 105 | uint64_t GetTimeMsec() 106 | { 107 | #ifdef _WIN32 108 | LARGE_INTEGER timeStamp = {}; 109 | if (!::QueryPerformanceCounter(&timeStamp)) 110 | return 0; 111 | if (PerfFrequencyInverseMsec == 0.) 112 | InitPerfFrequencyInverse(); 113 | return (uint64_t)(PerfFrequencyInverseMsec * timeStamp.QuadPart); 114 | #else 115 | // TBD: Optimize this? 116 | return GetTimeUsec() / 1000; 117 | #endif 118 | } 119 | 120 | 121 | } // namespace siamese 122 | -------------------------------------------------------------------------------- /SiameseTools.h: -------------------------------------------------------------------------------- 1 | /** \file 2 | \brief Siamese FEC Implementation: Tools 3 | \copyright Copyright (c) 2017 Christopher A. Taylor. All rights reserved. 4 | 5 | Redistribution and use in source and binary forms, with or without 6 | modification, are permitted provided that the following conditions are met: 7 | 8 | * Redistributions of source code must retain the above copyright notice, 9 | this list of conditions and the following disclaimer. 10 | * Redistributions in binary form must reproduce the above copyright notice, 11 | this list of conditions and the following disclaimer in the documentation 12 | and/or other materials provided with the distribution. 13 | * Neither the name of Siamese nor the names of its contributors may be 14 | used to endorse or promote products derived from this software without 15 | specific prior written permission. 16 | 17 | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" 18 | AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 19 | IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 20 | ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE 21 | LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR 22 | CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF 23 | SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS 24 | INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN 25 | CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) 26 | ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE 27 | POSSIBILITY OF SUCH DAMAGE. 28 | */ 29 | 30 | #pragma once 31 | 32 | /** 33 | Tools: 34 | 35 | + System headers 36 | + Debug breakpoints/asserts 37 | + Compiler-specific code wrappers 38 | + PCGRandom implementation 39 | + Microsecond timing 40 | + Windowed minimum/maximum 41 | */ 42 | 43 | #include // uint32_t 44 | #include // memcpy 45 | #include // std::nothrow 46 | 47 | 48 | //------------------------------------------------------------------------------ 49 | // Portability macros 50 | 51 | // Compiler-specific debug break 52 | #if defined(_DEBUG) || defined(DEBUG) 53 | #define SIAMESE_DEBUG 54 | #ifdef _WIN32 55 | #define SIAMESE_DEBUG_BREAK() __debugbreak() 56 | #else 57 | #define SIAMESE_DEBUG_BREAK() __builtin_trap() 58 | #endif 59 | #define SIAMESE_DEBUG_ASSERT(cond) { if (!(cond)) { SIAMESE_DEBUG_BREAK(); } } 60 | #else 61 | #define SIAMESE_DEBUG_BREAK() do {} while (false); 62 | #define SIAMESE_DEBUG_ASSERT(cond) do {} while (false); 63 | #endif 64 | 65 | // Compiler-specific force inline keyword 66 | #ifdef _MSC_VER 67 | #define SIAMESE_FORCE_INLINE inline __forceinline 68 | #else 69 | #define SIAMESE_FORCE_INLINE inline __attribute__((always_inline)) 70 | #endif 71 | 72 | 73 | namespace siamese { 74 | 75 | 76 | //------------------------------------------------------------------------------ 77 | // PCG PRNG 78 | 79 | /// From http://www.pcg-random.org/ 80 | class PCGRandom 81 | { 82 | public: 83 | void Seed(uint64_t y, uint64_t x = 0) 84 | { 85 | State = 0; 86 | Inc = (y << 1u) | 1u; 87 | Next(); 88 | State += x; 89 | Next(); 90 | } 91 | 92 | uint32_t Next() 93 | { 94 | const uint64_t oldstate = State; 95 | State = oldstate * UINT64_C(6364136223846793005) + Inc; 96 | const uint32_t xorshifted = (uint32_t)(((oldstate >> 18) ^ oldstate) >> 27); 97 | const uint32_t rot = oldstate >> 59; 98 | return (xorshifted >> rot) | (xorshifted << ((uint32_t)(-(int32_t)rot) & 31)); 99 | } 100 | 101 | uint64_t State = 0, Inc = 0; 102 | }; 103 | 104 | 105 | //------------------------------------------------------------------------------ 106 | // Timing 107 | 108 | /// Platform independent high-resolution timers 109 | uint64_t GetTimeUsec(); 110 | uint64_t GetTimeMsec(); 111 | 112 | 113 | //------------------------------------------------------------------------------ 114 | // WindowedMinMax 115 | 116 | template struct WindowedMinCompare 117 | { 118 | SIAMESE_FORCE_INLINE bool operator()(const T x, const T y) const 119 | { 120 | return x <= y; 121 | } 122 | }; 123 | 124 | template struct WindowedMaxCompare 125 | { 126 | SIAMESE_FORCE_INLINE bool operator()(const T x, const T y) const 127 | { 128 | return x >= y; 129 | } 130 | }; 131 | 132 | /// Templated class that calculates a running windowed minimum or maximum with 133 | /// a fixed time and resource cost. 134 | template class WindowedMinMax 135 | { 136 | public: 137 | typedef uint64_t TimeT; 138 | CompareT Compare; 139 | 140 | struct Sample 141 | { 142 | /// Sample value 143 | T Value; 144 | 145 | /// Timestamp of data collection 146 | TimeT Timestamp; 147 | 148 | 149 | /// Default values and initializing constructor 150 | explicit Sample(T value = 0, TimeT timestamp = 0) 151 | : Value(value) 152 | , Timestamp(timestamp) 153 | { 154 | } 155 | 156 | /// Check if a timeout expired 157 | inline bool TimeoutExpired(TimeT now, TimeT timeout) 158 | { 159 | return (TimeT)(now - Timestamp) > timeout; 160 | } 161 | }; 162 | 163 | 164 | static const unsigned kSampleCount = 3; 165 | 166 | Sample Samples[kSampleCount]; 167 | 168 | 169 | bool IsValid() const 170 | { 171 | return Samples[0].Value != 0; ///< ish 172 | } 173 | 174 | T GetBest() const 175 | { 176 | return Samples[0].Value; 177 | } 178 | 179 | void Reset(const Sample sample = Sample()) 180 | { 181 | Samples[0] = Samples[1] = Samples[2] = sample; 182 | } 183 | 184 | void Update(T value, TimeT timestamp, const TimeT windowLengthTime) 185 | { 186 | const Sample sample(value, timestamp); 187 | 188 | // On the first sample, new best sample, or if window length has expired: 189 | if (!IsValid() || 190 | Compare(value, Samples[0].Value) || 191 | Samples[2].TimeoutExpired(sample.Timestamp, windowLengthTime)) 192 | { 193 | Reset(sample); 194 | return; 195 | } 196 | 197 | // Insert the new value into the sorted array 198 | if (Compare(value, Samples[1].Value)) 199 | Samples[2] = Samples[1] = sample; 200 | else if (Compare(value, Samples[2].Value)) 201 | Samples[2] = sample; 202 | 203 | // Expire best if it has been the best for a long time 204 | if (Samples[0].TimeoutExpired(sample.Timestamp, windowLengthTime)) 205 | { 206 | // Also expire the next best if needed 207 | if (Samples[1].TimeoutExpired(sample.Timestamp, windowLengthTime)) 208 | { 209 | Samples[0] = Samples[2]; 210 | Samples[1] = sample; 211 | } 212 | else 213 | { 214 | Samples[0] = Samples[1]; 215 | Samples[1] = Samples[2]; 216 | } 217 | Samples[2] = sample; 218 | return; 219 | } 220 | 221 | // Quarter of window has gone by without a better value - Use the second-best 222 | if (Samples[1].Value == Samples[0].Value && 223 | Samples[1].TimeoutExpired(sample.Timestamp, windowLengthTime / 4)) 224 | { 225 | Samples[2] = Samples[1] = sample; 226 | return; 227 | } 228 | 229 | // Half the window has gone by without a better value - Use the third-best one 230 | if (Samples[2].Value == Samples[1].Value && 231 | Samples[2].TimeoutExpired(sample.Timestamp, windowLengthTime / 2)) 232 | { 233 | Samples[2] = sample; 234 | } 235 | } 236 | }; 237 | 238 | 239 | } // namespace siamese 240 | -------------------------------------------------------------------------------- /gf256.h: -------------------------------------------------------------------------------- 1 | /** \file 2 | \brief GF(256) Main C API Header 3 | \copyright Copyright (c) 2017 Christopher A. Taylor. All rights reserved. 4 | 5 | Redistribution and use in source and binary forms, with or without 6 | modification, are permitted provided that the following conditions are met: 7 | 8 | * Redistributions of source code must retain the above copyright notice, 9 | this list of conditions and the following disclaimer. 10 | * Redistributions in binary form must reproduce the above copyright notice, 11 | this list of conditions and the following disclaimer in the documentation 12 | and/or other materials provided with the distribution. 13 | * Neither the name of GF256 nor the names of its contributors may be 14 | used to endorse or promote products derived from this software without 15 | specific prior written permission. 16 | 17 | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" 18 | AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 19 | IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 20 | ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE 21 | LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR 22 | CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF 23 | SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS 24 | INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN 25 | CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) 26 | ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE 27 | POSSIBILITY OF SUCH DAMAGE. 28 | */ 29 | 30 | #ifndef CAT_GF256_H 31 | #define CAT_GF256_H 32 | 33 | /** \page GF256 GF(256) Math Module 34 | 35 | This module provides efficient implementations of bulk 36 | GF(2^^8) math operations over memory buffers. 37 | 38 | Addition is done over the base field in GF(2) meaning 39 | that addition is XOR between memory buffers. 40 | 41 | Multiplication is performed using table lookups via 42 | SIMD instructions. This is somewhat slower than XOR, 43 | but fast enough to not become a major bottleneck when 44 | used sparingly. 45 | */ 46 | 47 | #include // uint32_t etc 48 | #include // memcpy, memset 49 | 50 | /// Library header version 51 | #define GF256_VERSION 2 52 | 53 | //------------------------------------------------------------------------------ 54 | // Platform/Architecture 55 | 56 | #if defined(ANDROID) || defined(IOS) || defined(LINUX_ARM) || defined(__powerpc__) || defined(__s390__) 57 | #define GF256_TARGET_MOBILE 58 | #endif // ANDROID 59 | 60 | #if defined(__AVX2__) || (defined (_MSC_VER) && _MSC_VER >= 1900) 61 | #define GF256_TRY_AVX2 /* 256-bit */ 62 | #include 63 | #define GF256_ALIGN_BYTES 32 64 | #else // __AVX2__ 65 | #define GF256_ALIGN_BYTES 16 66 | #endif // __AVX2__ 67 | 68 | #if !defined(GF256_TARGET_MOBILE) 69 | // Note: MSVC currently only supports SSSE3 but not AVX2 70 | #include // SSSE3: _mm_shuffle_epi8 71 | #include // SSE2 72 | #endif // GF256_TARGET_MOBILE 73 | 74 | #if defined(HAVE_ARM_NEON_H) 75 | #include 76 | #endif // HAVE_ARM_NEON_H 77 | 78 | #if defined(GF256_TARGET_MOBILE) 79 | 80 | #define GF256_ALIGNED_ACCESSES /* Inputs must be aligned to GF256_ALIGN_BYTES */ 81 | 82 | # if defined(HAVE_ARM_NEON_H) 83 | // Compiler-specific 128-bit SIMD register keyword 84 | #define GF256_M128 uint8x16_t 85 | #define GF256_TRY_NEON 86 | #else 87 | #define GF256_M128 uint64_t 88 | # endif 89 | 90 | #else // GF256_TARGET_MOBILE 91 | 92 | // Compiler-specific 128-bit SIMD register keyword 93 | #define GF256_M128 __m128i 94 | 95 | #endif // GF256_TARGET_MOBILE 96 | 97 | #ifdef GF256_TRY_AVX2 98 | // Compiler-specific 256-bit SIMD register keyword 99 | #define GF256_M256 __m256i 100 | #endif 101 | 102 | // Compiler-specific C++11 restrict keyword 103 | #define GF256_RESTRICT __restrict 104 | 105 | // Compiler-specific force inline keyword 106 | #ifdef _MSC_VER 107 | #define GF256_FORCE_INLINE inline __forceinline 108 | #else 109 | #define GF256_FORCE_INLINE inline __attribute__((always_inline)) 110 | #endif 111 | 112 | // Compiler-specific alignment keyword 113 | // Note: Alignment only matters for ARM NEON where it should be 16 114 | #ifdef _MSC_VER 115 | #define GF256_ALIGNED __declspec(align(GF256_ALIGN_BYTES)) 116 | #else // _MSC_VER 117 | #define GF256_ALIGNED __attribute__((aligned(GF256_ALIGN_BYTES))) 118 | #endif // _MSC_VER 119 | 120 | #ifdef __cplusplus 121 | extern "C" { 122 | #endif // __cplusplus 123 | 124 | 125 | //------------------------------------------------------------------------------ 126 | // Portability 127 | 128 | /// Swap two memory buffers in-place 129 | extern void gf256_memswap(void * GF256_RESTRICT vx, void * GF256_RESTRICT vy, int bytes); 130 | 131 | 132 | //------------------------------------------------------------------------------ 133 | // GF(256) Context 134 | 135 | #ifdef _MSC_VER 136 | #pragma warning(push) 137 | #pragma warning(disable: 4324) // warning C4324: 'gf256_ctx' : structure was padded due to __declspec(align()) 138 | #endif // _MSC_VER 139 | 140 | /// The context object stores tables required to perform library calculations 141 | struct gf256_ctx 142 | { 143 | /// We require memory to be aligned since the SIMD instructions benefit from 144 | /// or require aligned accesses to the table data. 145 | struct 146 | { 147 | GF256_ALIGNED GF256_M128 TABLE_LO_Y[256]; 148 | GF256_ALIGNED GF256_M128 TABLE_HI_Y[256]; 149 | } MM128; 150 | #ifdef GF256_TRY_AVX2 151 | struct 152 | { 153 | GF256_ALIGNED GF256_M256 TABLE_LO_Y[256]; 154 | GF256_ALIGNED GF256_M256 TABLE_HI_Y[256]; 155 | } MM256; 156 | #endif // GF256_TRY_AVX2 157 | 158 | /// Mul/Div/Inv/Sqr tables 159 | uint8_t GF256_MUL_TABLE[256 * 256]; 160 | uint8_t GF256_DIV_TABLE[256 * 256]; 161 | uint8_t GF256_INV_TABLE[256]; 162 | uint8_t GF256_SQR_TABLE[256]; 163 | 164 | /// Log/Exp tables 165 | uint16_t GF256_LOG_TABLE[256]; 166 | uint8_t GF256_EXP_TABLE[512 * 2 + 1]; 167 | 168 | /// Polynomial used 169 | unsigned Polynomial; 170 | }; 171 | 172 | #ifdef _MSC_VER 173 | #pragma warning(pop) 174 | #endif // _MSC_VER 175 | 176 | extern gf256_ctx GF256Ctx; 177 | 178 | 179 | //------------------------------------------------------------------------------ 180 | // Initialization 181 | 182 | /** 183 | Initialize a context, filling in the tables. 184 | 185 | Thread-safety / Usage Notes: 186 | 187 | It is perfectly safe and encouraged to use a gf256_ctx object from multiple 188 | threads. The gf256_init() is relatively expensive and should only be done 189 | once, though it will take less than a millisecond. 190 | 191 | The gf256_ctx object must be aligned to 16 byte boundary. 192 | Simply tag the object with GF256_ALIGNED to achieve this. 193 | 194 | Example: 195 | static GF256_ALIGNED gf256_ctx TheGF256Context; 196 | gf256_init(&TheGF256Context, 0); 197 | 198 | Returns 0 on success and other values on failure. 199 | */ 200 | extern int gf256_init_(int version); 201 | #define gf256_init() gf256_init_(GF256_VERSION) 202 | 203 | 204 | //------------------------------------------------------------------------------ 205 | // Math Operations 206 | 207 | /// return x + y 208 | static GF256_FORCE_INLINE uint8_t gf256_add(uint8_t x, uint8_t y) 209 | { 210 | return (uint8_t)(x ^ y); 211 | } 212 | 213 | /// return x * y 214 | /// For repeated multiplication by a constant, it is faster to put the constant in y. 215 | static GF256_FORCE_INLINE uint8_t gf256_mul(uint8_t x, uint8_t y) 216 | { 217 | return GF256Ctx.GF256_MUL_TABLE[((unsigned)y << 8) + x]; 218 | } 219 | 220 | /// return x / y 221 | /// Memory-access optimized for constant divisors in y. 222 | static GF256_FORCE_INLINE uint8_t gf256_div(uint8_t x, uint8_t y) 223 | { 224 | return GF256Ctx.GF256_DIV_TABLE[((unsigned)y << 8) + x]; 225 | } 226 | 227 | /// return 1 / x 228 | static GF256_FORCE_INLINE uint8_t gf256_inv(uint8_t x) 229 | { 230 | return GF256Ctx.GF256_INV_TABLE[x]; 231 | } 232 | 233 | /// return x * x 234 | static GF256_FORCE_INLINE uint8_t gf256_sqr(uint8_t x) 235 | { 236 | return GF256Ctx.GF256_SQR_TABLE[x]; 237 | } 238 | 239 | 240 | //------------------------------------------------------------------------------ 241 | // Bulk Memory Math Operations 242 | 243 | /// Performs "x[] += y[]" bulk memory XOR operation 244 | extern void gf256_add_mem(void * GF256_RESTRICT vx, 245 | const void * GF256_RESTRICT vy, int bytes); 246 | 247 | /// Performs "z[] += x[] + y[]" bulk memory operation 248 | extern void gf256_add2_mem(void * GF256_RESTRICT vz, const void * GF256_RESTRICT vx, 249 | const void * GF256_RESTRICT vy, int bytes); 250 | 251 | /// Performs "z[] = x[] + y[]" bulk memory operation 252 | extern void gf256_addset_mem(void * GF256_RESTRICT vz, const void * GF256_RESTRICT vx, 253 | const void * GF256_RESTRICT vy, int bytes); 254 | 255 | /// Performs "z[] = x[] * y" bulk memory operation 256 | extern void gf256_mul_mem(void * GF256_RESTRICT vz, 257 | const void * GF256_RESTRICT vx, uint8_t y, int bytes); 258 | 259 | /// Performs "z[] += x[] * y" bulk memory operation 260 | extern void gf256_muladd_mem(void * GF256_RESTRICT vz, uint8_t y, 261 | const void * GF256_RESTRICT vx, int bytes); 262 | 263 | /// Performs "x[] /= y" bulk memory operation 264 | static GF256_FORCE_INLINE void gf256_div_mem(void * GF256_RESTRICT vz, 265 | const void * GF256_RESTRICT vx, uint8_t y, int bytes) 266 | { 267 | // Multiply by inverse 268 | gf256_mul_mem(vz, vx, y == 1 ? (uint8_t)1 : GF256Ctx.GF256_INV_TABLE[y], bytes); 269 | } 270 | 271 | 272 | //------------------------------------------------------------------------------ 273 | // Misc Operations 274 | 275 | /// Swap two memory buffers in-place 276 | extern void gf256_memswap(void * GF256_RESTRICT vx, void * GF256_RESTRICT vy, int bytes); 277 | 278 | 279 | #ifdef __cplusplus 280 | } 281 | #endif // __cplusplus 282 | 283 | #endif // CAT_GF256_H 284 | -------------------------------------------------------------------------------- /proj/msvc/Siamese.sln: -------------------------------------------------------------------------------- 1 |  2 | Microsoft Visual Studio Solution File, Format Version 12.00 3 | # Visual Studio 14 4 | VisualStudioVersion = 14.0.25420.1 5 | MinimumVisualStudioVersion = 10.0.40219.1 6 | Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "Siamese", "SiameseLib.vcxproj", "{FF5912EF-7424-4974-B877-62B03D5046C6}" 7 | EndProject 8 | Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "SiameseInternalTests", "..\..\tests\msvc\SiameseInternalTests.vcxproj", "{E28D52C7-2A45-4E7E-86B5-75EA2F579C3E}" 9 | EndProject 10 | Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "SiameseUnitTests", "..\..\tests\msvc\SiameseUnitTest.vcxproj", "{32176592-2F30-4BD5-B645-EB11C8D3453E}" 11 | ProjectSection(ProjectDependencies) = postProject 12 | {FF5912EF-7424-4974-B877-62B03D5046C6} = {FF5912EF-7424-4974-B877-62B03D5046C6} 13 | EndProjectSection 14 | EndProject 15 | Global 16 | GlobalSection(SolutionConfigurationPlatforms) = preSolution 17 | Debug|Win32 = Debug|Win32 18 | Debug|x64 = Debug|x64 19 | Release|Win32 = Release|Win32 20 | Release|x64 = Release|x64 21 | EndGlobalSection 22 | GlobalSection(ProjectConfigurationPlatforms) = postSolution 23 | {FF5912EF-7424-4974-B877-62B03D5046C6}.Debug|Win32.ActiveCfg = Debug|Win32 24 | {FF5912EF-7424-4974-B877-62B03D5046C6}.Debug|Win32.Build.0 = Debug|Win32 25 | {FF5912EF-7424-4974-B877-62B03D5046C6}.Debug|x64.ActiveCfg = Debug|x64 26 | {FF5912EF-7424-4974-B877-62B03D5046C6}.Debug|x64.Build.0 = Debug|x64 27 | {FF5912EF-7424-4974-B877-62B03D5046C6}.Release|Win32.ActiveCfg = Release|Win32 28 | {FF5912EF-7424-4974-B877-62B03D5046C6}.Release|Win32.Build.0 = Release|Win32 29 | {FF5912EF-7424-4974-B877-62B03D5046C6}.Release|x64.ActiveCfg = Release|x64 30 | {FF5912EF-7424-4974-B877-62B03D5046C6}.Release|x64.Build.0 = Release|x64 31 | {E28D52C7-2A45-4E7E-86B5-75EA2F579C3E}.Debug|Win32.ActiveCfg = Debug|Win32 32 | {E28D52C7-2A45-4E7E-86B5-75EA2F579C3E}.Debug|Win32.Build.0 = Debug|Win32 33 | {E28D52C7-2A45-4E7E-86B5-75EA2F579C3E}.Debug|x64.ActiveCfg = Debug|x64 34 | {E28D52C7-2A45-4E7E-86B5-75EA2F579C3E}.Debug|x64.Build.0 = Debug|x64 35 | {E28D52C7-2A45-4E7E-86B5-75EA2F579C3E}.Release|Win32.ActiveCfg = Release|Win32 36 | {E28D52C7-2A45-4E7E-86B5-75EA2F579C3E}.Release|Win32.Build.0 = Release|Win32 37 | {E28D52C7-2A45-4E7E-86B5-75EA2F579C3E}.Release|x64.ActiveCfg = Release|x64 38 | {E28D52C7-2A45-4E7E-86B5-75EA2F579C3E}.Release|x64.Build.0 = Release|x64 39 | {32176592-2F30-4BD5-B645-EB11C8D3453E}.Debug|Win32.ActiveCfg = Debug|Win32 40 | {32176592-2F30-4BD5-B645-EB11C8D3453E}.Debug|Win32.Build.0 = Debug|Win32 41 | {32176592-2F30-4BD5-B645-EB11C8D3453E}.Debug|x64.ActiveCfg = Debug|x64 42 | {32176592-2F30-4BD5-B645-EB11C8D3453E}.Debug|x64.Build.0 = Debug|x64 43 | {32176592-2F30-4BD5-B645-EB11C8D3453E}.Release|Win32.ActiveCfg = Release|Win32 44 | {32176592-2F30-4BD5-B645-EB11C8D3453E}.Release|Win32.Build.0 = Release|Win32 45 | {32176592-2F30-4BD5-B645-EB11C8D3453E}.Release|x64.ActiveCfg = Release|x64 46 | {32176592-2F30-4BD5-B645-EB11C8D3453E}.Release|x64.Build.0 = Release|x64 47 | EndGlobalSection 48 | GlobalSection(SolutionProperties) = preSolution 49 | HideSolutionNode = FALSE 50 | EndGlobalSection 51 | EndGlobal 52 | -------------------------------------------------------------------------------- /proj/msvc/SiameseLib.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 | {FF5912EF-7424-4974-B877-62B03D5046C6} 23 | SiameseLib 24 | Siamese 25 | 10.0.17134.0 26 | 27 | 28 | 29 | StaticLibrary 30 | true 31 | MultiByte 32 | v141 33 | 34 | 35 | StaticLibrary 36 | true 37 | MultiByte 38 | v141 39 | 40 | 41 | StaticLibrary 42 | false 43 | true 44 | MultiByte 45 | v141 46 | 47 | 48 | StaticLibrary 49 | false 50 | true 51 | MultiByte 52 | v141 53 | 54 | 55 | 56 | 57 | 58 | 59 | 60 | 61 | 62 | 63 | 64 | 65 | 66 | 67 | 68 | 69 | 70 | 71 | Output/$(ProjectName)/$(Configuration)/$(Platform)/ 72 | Obj/$(ProjectName)/$(Configuration)/$(Platform)/ 73 | 74 | 75 | Output/$(ProjectName)/$(Configuration)/$(Platform)/ 76 | Obj/$(ProjectName)/$(Configuration)/$(Platform)/ 77 | 78 | 79 | Output/$(ProjectName)/$(Configuration)/$(Platform)/ 80 | Obj/$(ProjectName)/$(Configuration)/$(Platform)/ 81 | 82 | 83 | Output/$(ProjectName)/$(Configuration)/$(Platform)/ 84 | Obj/$(ProjectName)/$(Configuration)/$(Platform)/ 85 | 86 | 87 | 88 | Level4 89 | Disabled 90 | true 91 | SIAMESE_BUILDING;%(PreprocessorDefinitions) 92 | true 93 | MultiThreadedDebug 94 | 95 | 96 | true 97 | $(OutDir)$(TargetName)$(TargetExt) 98 | 99 | 100 | 101 | 102 | 103 | 104 | 105 | 106 | Level4 107 | Disabled 108 | true 109 | SIAMESE_BUILDING;%(PreprocessorDefinitions) 110 | true 111 | MultiThreadedDebug 112 | 113 | 114 | true 115 | $(OutDir)$(TargetName)$(TargetExt) 116 | 117 | 118 | 119 | 120 | 121 | 122 | 123 | 124 | Level4 125 | Full 126 | true 127 | true 128 | true 129 | MultiThreaded 130 | false 131 | AnySuitable 132 | Speed 133 | true 134 | SIAMESE_BUILDING;%(PreprocessorDefinitions) 135 | true 136 | true 137 | false 138 | 139 | 140 | true 141 | true 142 | true 143 | $(OutDir)$(TargetName)$(TargetExt) 144 | 145 | 146 | 147 | 148 | 149 | 150 | 151 | 152 | Level4 153 | Full 154 | true 155 | true 156 | true 157 | MultiThreaded 158 | false 159 | AnySuitable 160 | Speed 161 | true 162 | SIAMESE_BUILDING;%(PreprocessorDefinitions) 163 | true 164 | true 165 | false 166 | 167 | 168 | true 169 | true 170 | true 171 | $(OutDir)$(TargetName)$(TargetExt) 172 | 173 | 174 | 175 | 176 | 177 | 178 | 179 | 180 | 181 | 182 | 183 | 184 | 185 | 186 | 187 | 188 | 189 | 190 | 191 | 192 | 193 | 194 | 195 | 196 | 197 | 198 | 199 | 200 | 201 | 202 | 203 | 204 | 205 | -------------------------------------------------------------------------------- /proj/msvc/SiameseLib.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 | Source Files 29 | 30 | 31 | Source Files 32 | 33 | 34 | Source Files 35 | 36 | 37 | Source Files 38 | 39 | 40 | Source Files 41 | 42 | 43 | 44 | 45 | Source Files 46 | 47 | 48 | Source Files 49 | 50 | 51 | Source Files 52 | 53 | 54 | Source Files 55 | 56 | 57 | Source Files 58 | 59 | 60 | Header Files 61 | 62 | 63 | Source Files 64 | 65 | 66 | Source Files 67 | 68 | 69 | Source Files 70 | 71 | 72 | 73 | 74 | Header Files 75 | 76 | 77 | -------------------------------------------------------------------------------- /siamese.cpp: -------------------------------------------------------------------------------- 1 | /** \file 2 | \brief Siamese Streaming Erasure Code Main C API Source 3 | \copyright Copyright (c) 2017 Christopher A. Taylor. All rights reserved. 4 | 5 | Redistribution and use in source and binary forms, with or without 6 | modification, are permitted provided that the following conditions are met: 7 | 8 | * Redistributions of source code must retain the above copyright notice, 9 | this list of conditions and the following disclaimer. 10 | * Redistributions in binary form must reproduce the above copyright notice, 11 | this list of conditions and the following disclaimer in the documentation 12 | and/or other materials provided with the distribution. 13 | * Neither the name of Siamese nor the names of its contributors may be 14 | used to endorse or promote products derived from this software without 15 | specific prior written permission. 16 | 17 | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" 18 | AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 19 | IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 20 | ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE 21 | LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR 22 | CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF 23 | SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS 24 | INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN 25 | CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) 26 | ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE 27 | POSSIBILITY OF SUCH DAMAGE. 28 | */ 29 | 30 | #include "siamese.h" 31 | 32 | #include "SiameseEncoder.h" 33 | #include "SiameseDecoder.h" 34 | 35 | extern "C" { 36 | 37 | 38 | //------------------------------------------------------------------------------ 39 | // Initialization API 40 | 41 | static bool m_Initialized = false; 42 | 43 | SIAMESE_EXPORT int siamese_init_(int version) 44 | { 45 | if (version != SIAMESE_VERSION) 46 | return Siamese_Disabled; 47 | 48 | if (0 != gf256_init()) 49 | return Siamese_Disabled; 50 | 51 | m_Initialized = true; 52 | return Siamese_Success; 53 | } 54 | 55 | 56 | //------------------------------------------------------------------------------ 57 | // Encoder API 58 | 59 | SIAMESE_EXPORT SiameseEncoder siamese_encoder_create() 60 | { 61 | SIAMESE_DEBUG_ASSERT(m_Initialized); // Must call siamese_init() first 62 | if (!m_Initialized) 63 | return nullptr; 64 | 65 | siamese::Encoder* encoder = new(std::nothrow) siamese::Encoder; 66 | 67 | return reinterpret_cast(encoder); 68 | } 69 | 70 | SIAMESE_EXPORT void siamese_encoder_free( 71 | SiameseEncoder encoder_t) 72 | { 73 | siamese::Encoder* encoder = reinterpret_cast(encoder_t); 74 | delete encoder; 75 | } 76 | 77 | SIAMESE_EXPORT SiameseResult siamese_encoder_is_ready( 78 | SiameseEncoder encoder_t ///< [in] Encoder to check 79 | ) 80 | { 81 | siamese::Encoder* encoder = reinterpret_cast(encoder_t); 82 | if (!encoder) { 83 | return Siamese_InvalidInput; 84 | } 85 | 86 | // Provide a buffer of some packets, to allow the application some slack 87 | // to add two packets after checking. Otherwise it's pretty easy for 88 | // app developers to write code that accidentally does that in practice. 89 | if (encoder->GetRemainingSlots() <= 2) { 90 | return Siamese_MaxPacketsReached; 91 | } 92 | 93 | return Siamese_Success; 94 | } 95 | 96 | SIAMESE_EXPORT SiameseResult siamese_encoder_add( 97 | SiameseEncoder encoder_t, 98 | SiameseOriginalPacket* packet) 99 | { 100 | siamese::Encoder* encoder = reinterpret_cast(encoder_t); 101 | if (!encoder || !packet || !packet->Data || 102 | packet->DataBytes <= 0 || packet->DataBytes > SIAMESE_MAX_PACKET_BYTES) 103 | { 104 | return Siamese_InvalidInput; 105 | } 106 | 107 | return encoder->Add(*packet); 108 | } 109 | 110 | SIAMESE_EXPORT SiameseResult siamese_encoder_get( 111 | SiameseEncoder encoder_t, 112 | SiameseOriginalPacket* packet) 113 | { 114 | siamese::Encoder* encoder = reinterpret_cast(encoder_t); 115 | if (!encoder || !packet || packet->PacketNum > SIAMESE_PACKET_NUM_MAX) 116 | { 117 | return Siamese_InvalidInput; 118 | } 119 | 120 | return encoder->Get(*packet); 121 | } 122 | 123 | SIAMESE_EXPORT SiameseResult siamese_encoder_remove_before( 124 | SiameseEncoder encoder_t, 125 | unsigned packetNum) 126 | { 127 | siamese::Encoder* encoder = reinterpret_cast(encoder_t); 128 | if (!encoder || packetNum > SIAMESE_PACKET_NUM_MAX) 129 | return Siamese_InvalidInput; 130 | 131 | encoder->RemoveBefore(packetNum); 132 | return Siamese_Success; 133 | } 134 | 135 | SIAMESE_EXPORT SiameseResult siamese_encoder_ack( 136 | SiameseEncoder encoder_t, 137 | const void* buffer, 138 | unsigned bytes, 139 | unsigned* nextExpectedPacketNum) 140 | { 141 | siamese::Encoder* encoder = reinterpret_cast(encoder_t); 142 | if (!encoder || !buffer || bytes < 1 || !nextExpectedPacketNum) 143 | return Siamese_InvalidInput; 144 | 145 | return encoder->Acknowledge((uint8_t*)buffer, bytes, *nextExpectedPacketNum); 146 | } 147 | 148 | SIAMESE_EXPORT SiameseResult siamese_encoder_retransmit( 149 | SiameseEncoder encoder_t, 150 | SiameseOriginalPacket* original) 151 | { 152 | siamese::Encoder* encoder = reinterpret_cast(encoder_t); 153 | if (!encoder || !original) 154 | return Siamese_InvalidInput; 155 | 156 | return encoder->Retransmit(*original); 157 | } 158 | 159 | SIAMESE_EXPORT SiameseResult siamese_encode( 160 | SiameseEncoder encoder_t, 161 | SiameseRecoveryPacket* recovery) 162 | { 163 | siamese::Encoder* encoder = reinterpret_cast(encoder_t); 164 | if (!encoder || !recovery) 165 | return Siamese_InvalidInput; 166 | 167 | return encoder->Encode(*recovery); 168 | } 169 | 170 | SIAMESE_EXPORT SiameseResult siamese_encoder_stats( 171 | SiameseEncoder encoder_t, 172 | uint64_t* statsOut, 173 | unsigned statsCount) 174 | { 175 | siamese::Encoder* encoder = reinterpret_cast(encoder_t); 176 | if (!encoder || !statsOut || statsCount <= 0) 177 | return Siamese_InvalidInput; 178 | 179 | return encoder->GetStatistics(statsOut, statsCount); 180 | } 181 | 182 | 183 | //------------------------------------------------------------------------------ 184 | // Decoder API 185 | 186 | SIAMESE_EXPORT SiameseDecoder siamese_decoder_create() 187 | { 188 | SIAMESE_DEBUG_ASSERT(m_Initialized); // Must call siamese_init() first 189 | if (!m_Initialized) 190 | return nullptr; 191 | 192 | siamese::Decoder* decoder = new(std::nothrow) siamese::Decoder; 193 | 194 | return reinterpret_cast(decoder); 195 | } 196 | 197 | SIAMESE_EXPORT void siamese_decoder_free( 198 | SiameseDecoder decoder_t) 199 | { 200 | siamese::Decoder* decoder = reinterpret_cast(decoder_t); 201 | if (decoder == nullptr) 202 | return; 203 | 204 | delete decoder; 205 | } 206 | 207 | SIAMESE_EXPORT SiameseResult siamese_decoder_add_original( 208 | SiameseDecoder decoder_t, 209 | const SiameseOriginalPacket* packet) 210 | { 211 | siamese::Decoder* decoder = reinterpret_cast(decoder_t); 212 | if (!decoder || !packet || packet->DataBytes <= 0 || 213 | packet->DataBytes > SIAMESE_MAX_PACKET_BYTES || 214 | packet->PacketNum > SIAMESE_PACKET_NUM_MAX) 215 | { 216 | return Siamese_InvalidInput; 217 | } 218 | 219 | return decoder->AddOriginal(*packet); 220 | } 221 | 222 | SIAMESE_EXPORT SiameseResult siamese_decoder_add_recovery( 223 | SiameseDecoder decoder_t, 224 | const SiameseRecoveryPacket* packet) 225 | { 226 | siamese::Decoder* decoder = reinterpret_cast(decoder_t); 227 | if (!decoder || !packet || !packet->Data || packet->DataBytes <= 0 || 228 | packet->DataBytes > SIAMESE_MAX_PACKET_BYTES /* extra check to avoid integer overflows */) 229 | { 230 | return Siamese_InvalidInput; 231 | } 232 | 233 | return decoder->AddRecovery(*packet); 234 | } 235 | 236 | SIAMESE_EXPORT SiameseResult siamese_decoder_get( 237 | SiameseDecoder decoder_t, 238 | SiameseOriginalPacket* packet) 239 | { 240 | siamese::Decoder* decoder = reinterpret_cast(decoder_t); 241 | if (!decoder || !packet || packet->PacketNum > SIAMESE_PACKET_NUM_MAX) 242 | return Siamese_InvalidInput; 243 | 244 | return decoder->Get(*packet); 245 | } 246 | 247 | SIAMESE_EXPORT SiameseResult siamese_decoder_is_ready( 248 | SiameseDecoder decoder_t) 249 | { 250 | siamese::Decoder* decoder = reinterpret_cast(decoder_t); 251 | if (!decoder) 252 | return Siamese_InvalidInput; 253 | 254 | return decoder->IsReadyToDecode(); 255 | } 256 | 257 | SIAMESE_EXPORT SiameseResult siamese_decode( 258 | SiameseDecoder decoder_t, 259 | SiameseOriginalPacket** packetsPtrOut, 260 | unsigned* countOut) 261 | { 262 | siamese::Decoder* decoder = reinterpret_cast(decoder_t); 263 | if (!decoder || (!packetsPtrOut != !countOut)) 264 | return Siamese_InvalidInput; 265 | 266 | return decoder->Decode( 267 | packetsPtrOut, 268 | countOut); 269 | } 270 | 271 | SIAMESE_EXPORT SiameseResult siamese_decoder_ack( 272 | SiameseDecoder decoder_t, 273 | void* buffer, 274 | unsigned byteLimit, 275 | unsigned* usedBytes) 276 | { 277 | siamese::Decoder* decoder = reinterpret_cast(decoder_t); 278 | if (!decoder || !buffer || !usedBytes || byteLimit < SIAMESE_ACK_MIN_BYTES) 279 | return Siamese_InvalidInput; 280 | 281 | return decoder->GenerateAcknowledgement( 282 | (uint8_t*)buffer, 283 | byteLimit, 284 | *usedBytes); 285 | } 286 | 287 | SIAMESE_EXPORT SiameseResult siamese_decoder_stats( 288 | SiameseDecoder decoder_t, 289 | uint64_t* statsOut, 290 | unsigned statsCount) 291 | { 292 | siamese::Decoder* decoder = reinterpret_cast(decoder_t); 293 | if (!decoder || !statsOut || statsCount <= 0) 294 | return Siamese_InvalidInput; 295 | 296 | return decoder->GetStatistics( 297 | statsOut, 298 | statsCount); 299 | } 300 | 301 | 302 | } // extern "C" 303 | -------------------------------------------------------------------------------- /tests/GF256Matrix.cpp: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright (c) 2016 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 Siamese 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 "GF256Matrix.h" 30 | 31 | #include 32 | #include "../gf256.h" 33 | 34 | #include 35 | #include 36 | using namespace std; 37 | 38 | 39 | //----------------------------------------------------------------------------- 40 | // GF256Matrix 41 | 42 | GF256Matrix::GF256Matrix() 43 | { 44 | _matrix = nullptr; 45 | } 46 | 47 | GF256Matrix::~GF256Matrix() 48 | { 49 | cleanup(); 50 | } 51 | 52 | bool GF256Matrix::Initialize(int rows, int cols) 53 | { 54 | if (rows <= 0 || cols <= 0) 55 | return false; 56 | 57 | cleanup(); 58 | 59 | _rows = rows; 60 | _cols = cols; 61 | int bytes = rows * cols; 62 | _matrix = new (std::nothrow)uint8_t[bytes]; 63 | if (!_matrix) 64 | return false; 65 | 66 | _pivot = new (std::nothrow)int[_rows]; 67 | if (!_pivot) 68 | return false; 69 | 70 | Zero(); 71 | 72 | return true; 73 | } 74 | 75 | void GF256Matrix::cleanup() 76 | { 77 | if (_matrix) 78 | { 79 | delete[]_matrix; 80 | _matrix = nullptr; 81 | } 82 | if (_pivot) 83 | { 84 | delete[]_pivot; 85 | _pivot = nullptr; 86 | } 87 | } 88 | 89 | void GF256Matrix::Zero() 90 | { 91 | memset(_matrix, 0, _rows * _cols); 92 | } 93 | 94 | void GF256Matrix::Print(int count) 95 | { 96 | cout << endl << "GF256Matrix is (rows, cols = " << _rows << " x " << _cols << "):" << endl; 97 | 98 | for (int ii = 0; ii < _rows && ii < count; ++ii) 99 | { 100 | for (int jj = 0; jj < _cols; ++jj) 101 | { 102 | cout << hex << setfill('0') << setw(2) << (int)_matrix[_cols * ii + jj] << " "; 103 | //if (jj % 16 == 0) cout << endl; 104 | } 105 | cout << endl; 106 | } 107 | 108 | cout << dec << endl; 109 | } 110 | 111 | int GF256Matrix::Solve() 112 | { 113 | int failures = 0; 114 | 115 | // Initialize pivot array 116 | for (int pivot_i = 0; pivot_i < _rows; ++pivot_i) 117 | _pivot[pivot_i] = pivot_i; 118 | //_pivot[pivot_i] = _rows - 1 - pivot_i; 119 | 120 | // For each pivot to determine: 121 | for (int pivot_i = 0; pivot_i < _cols; ++pivot_i) 122 | { 123 | failures = 0; 124 | 125 | bool found = false; 126 | for (int pivot_j = pivot_i; pivot_j < _rows; ++pivot_j) 127 | { 128 | int ge_row_j = _pivot[pivot_j]; 129 | uint8_t *ge_row = _matrix + _cols * ge_row_j; 130 | 131 | if (ge_row[pivot_i] == 0) 132 | { 133 | if (pivot_j >= _cols - 1) 134 | { 135 | ++failures; 136 | } 137 | else 138 | { 139 | //cout << pivot_i << " of " << _cols << endl; 140 | } 141 | } 142 | else 143 | { 144 | found = true; 145 | 146 | // Swap out the pivot index for this one 147 | int temp = _pivot[pivot_i]; 148 | _pivot[pivot_i] = _pivot[pivot_j]; 149 | _pivot[pivot_j] = temp; 150 | 151 | // For each remaining unused row, 152 | for (int pivot_k = pivot_i + 1; pivot_k < _rows; ++pivot_k) 153 | { 154 | int ge_row_k = _pivot[pivot_k]; 155 | uint8_t *rem_row = _matrix + _cols * ge_row_k; 156 | 157 | if (rem_row[pivot_i]) 158 | { 159 | uint8_t x = gf256_div(rem_row[pivot_i], ge_row[pivot_i]); 160 | 161 | //gf256_muladd_mem(rem_row + pivot_i + 1, x, ge_row + pivot_i + 1, _cols - (pivot_i + 1)); 162 | gf256_muladd_mem(rem_row, x, ge_row, _cols); 163 | } 164 | } 165 | 166 | break; 167 | } 168 | } 169 | 170 | // If pivot could not be found, 171 | if (!found) 172 | { 173 | return -1; 174 | } 175 | } 176 | 177 | return failures; 178 | } 179 | -------------------------------------------------------------------------------- /tests/GF256Matrix.h: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright (c) 2016 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 Siamese 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 | #pragma once 30 | 31 | #include 32 | 33 | class GF256Matrix 34 | { 35 | public: 36 | GF256Matrix(); 37 | ~GF256Matrix(); 38 | 39 | inline uint8_t* Get(int row, int col) 40 | { 41 | return _matrix + row * _cols + col; 42 | } 43 | 44 | inline uint8_t* GetFront() { return _matrix; } 45 | inline int GetPitch() { return _cols; } 46 | 47 | inline int GetRows() { return _rows; } 48 | inline int GetCols() { return _cols; } 49 | 50 | inline int Size() { return _rows * _cols; } 51 | 52 | bool Initialize(int rows, int cols); 53 | 54 | void Zero(); 55 | 56 | // Works for matrices with more rows than columns 57 | // Returns -1 for no success 58 | // Returns number of rows that were linearly related to the rest 59 | int Solve(); 60 | 61 | void Print(int count = 0x7fffffff); 62 | 63 | protected: 64 | int _rows = 0, _cols = 0; 65 | uint8_t* _matrix = nullptr; 66 | int* _pivot = nullptr; 67 | 68 | void cleanup(); 69 | }; 70 | -------------------------------------------------------------------------------- /tests/TestTools.cpp: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright (c) 2016 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 Siamese 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 "TestTools.h" 30 | -------------------------------------------------------------------------------- /tests/TestTools.h: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright (c) 2016 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 Siamese 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 | #pragma once 30 | 31 | #include "../SiameseTools.h" 32 | #include "../gf256.h" 33 | #include "../SiameseCommon.h" 34 | #include "../SiameseSerializers.h" 35 | -------------------------------------------------------------------------------- /tests/_attic_/gentab_graycode.cpp: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright (c) 2016 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 Siamese 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 | using namespace std; 34 | 35 | #ifdef _WIN32 36 | #include 37 | #include 38 | #endif 39 | 40 | #include "../SiameseTools.h" 41 | 42 | #define ENABLE_GENTAB_GRAYCODE 43 | 44 | /* 45 | I did some exploration into different kinds of Gray codes to see if there was a better choice. 46 | The canonical Reflected Binary Code seems optimal for my use case, so I decided to implement that. 47 | */ 48 | 49 | static inline int only_one_bit_set_to_one(uint32_t b) 50 | { 51 | return b && !(b & (b - 1)); 52 | } 53 | 54 | static inline bool hamming1(uint32_t a, uint32_t b) 55 | { 56 | return only_one_bit_set_to_one(a ^ b) != 0; 57 | } 58 | 59 | static const int kBits = 5; 60 | static const int kTableSize = 1 << kBits; 61 | static uint8_t Table[kTableSize] = {}; 62 | 63 | static uint8_t AccumulatedPopCount[kTableSize]; 64 | static uint8_t BackAccumulatedPopCount[kTableSize]; 65 | 66 | static bool bestPopCount() 67 | { 68 | uint8_t popCount[kTableSize]; 69 | uint8_t acc = 0; 70 | for (int i = 0; i < kTableSize; ++i) 71 | { 72 | acc |= Table[i]; 73 | int pop = __popcnt(acc); 74 | if (pop > AccumulatedPopCount[i]) 75 | return false; 76 | popCount[i] = pop; 77 | } 78 | for (int i = 0; i < kTableSize; ++i) 79 | { 80 | AccumulatedPopCount[i] = popCount[i]; 81 | } 82 | return true; 83 | } 84 | 85 | static bool bestBackPopCount() 86 | { 87 | uint8_t popCount[kTableSize]; 88 | uint8_t acc = 0; 89 | for (int i = kTableSize - 1; i >= 0; --i) 90 | { 91 | acc |= Table[i]; 92 | int pop = __popcnt(acc); 93 | if (pop > BackAccumulatedPopCount[i]) 94 | return false; 95 | popCount[i] = pop; 96 | } 97 | for (int i = 0; i < kTableSize; ++i) 98 | { 99 | BackAccumulatedPopCount[i] = popCount[i]; 100 | } 101 | return true; 102 | } 103 | 104 | static void analyzeTable() 105 | { 106 | // Make sure just one bit is set at the end so we can concatenate the codes 107 | if (!only_one_bit_set_to_one(Table[kTableSize - 1])) 108 | { 109 | return; 110 | } 111 | 112 | if (!bestPopCount() || !bestBackPopCount()) 113 | { 114 | return; 115 | } 116 | 117 | uint8_t flipIndices[kTableSize - 1] = {}; 118 | for (int i = 1; i < kTableSize; ++i) 119 | { 120 | uint8_t delta = Table[i] ^ Table[i - 1]; 121 | if (!only_one_bit_set_to_one(delta)) 122 | { 123 | cout << "FAILURE" << endl; 124 | } 125 | unsigned long index = 0; 126 | if (1 != _BitScanForward(&index, delta)) 127 | { 128 | cout << "FAILURE" << endl; 129 | } 130 | flipIndices[i - 1] = (uint8_t)index; 131 | } 132 | 133 | if (flipIndices[4] != 1) 134 | return; 135 | if (flipIndices[3] != 2) 136 | return; 137 | 138 | cout << "static const uint8_t GrayCode[kTableSize] = { "; 139 | for (int i = 0; i < kTableSize; ++i) 140 | { 141 | cout << (int)Table[i]; 142 | if (i != kTableSize - 1) 143 | cout << ", "; 144 | } 145 | cout << " };" << endl; 146 | #if 1 147 | cout << "static const uint8_t AccumulatedPopCount[kTableSize] = { "; 148 | for (int i = 0; i < kTableSize; ++i) 149 | { 150 | cout << (int)AccumulatedPopCount[i]; 151 | 152 | if (i != kTableSize - 1) 153 | cout << ", "; 154 | } 155 | cout << " };" << endl; 156 | #endif 157 | #if 1 158 | cout << "static const uint8_t FlipIndices[kTableSize] = { "; 159 | for (int i = 1; i < kTableSize; ++i) 160 | { 161 | cout << (int)flipIndices[i - 1]; 162 | 163 | if (i != kTableSize - 1) 164 | cout << ", "; 165 | } 166 | cout << " };" << endl; 167 | for (int i = 0; i < kBits; ++i) 168 | { 169 | cout << "Flip " << i << " at: "; 170 | for (int j = 0; j < kTableSize; ++j) 171 | { 172 | if (flipIndices[j] == i) 173 | { 174 | cout << j + 1 << ", "; 175 | } 176 | } 177 | cout << endl; 178 | } 179 | #endif 180 | cout << endl; 181 | } 182 | 183 | static void shuffleTable(int offset) 184 | { 185 | if (offset == kTableSize) 186 | { 187 | analyzeTable(); 188 | return; 189 | } 190 | 191 | uint8_t y = Table[offset - 1]; 192 | 193 | for (int i = offset; i < kTableSize; ++i) 194 | { 195 | uint8_t x = Table[i]; 196 | 197 | if (hamming1(x, y)) 198 | { 199 | Table[i] = Table[offset]; 200 | Table[offset] = x; 201 | 202 | shuffleTable(offset + 1); 203 | 204 | Table[offset] = Table[i]; 205 | Table[i] = x; 206 | } 207 | } 208 | } 209 | 210 | static void GenerateGrayCodes() 211 | { 212 | for (int i = 0; i < kTableSize; ++i) 213 | { 214 | Table[i] = (uint8_t)i; 215 | AccumulatedPopCount[i] = 255; 216 | BackAccumulatedPopCount[i] = 255; 217 | } 218 | 219 | shuffleTable(2); 220 | } 221 | 222 | /* 223 | 4 00000 <- 0 224 | 0 00001 <- 1 225 | 1 00011 <- 2 226 | 0 00010 <- 3 227 | 2 00110 228 | 0 00111 229 | 1 00101 230 | 0 00100 231 | 3 01100 232 | 0 01101 233 | 1 01111 234 | 0 01110 235 | 2 01010 236 | 0 01011 237 | 1 01001 <- 14 238 | 0 01000 <- 15 239 | 4 11000 <- 16 240 | 0 11001 241 | 1 11011 242 | 0 11010 243 | 2 11110 244 | 0 11111 245 | 1 11101 246 | 0 11100 247 | 3 10100 248 | 0 10101 249 | 1 10111 250 | 0 10110 251 | 2 10010 252 | 0 10011 253 | 1 10001 254 | 0 10000 <- 31 255 | */ 256 | 257 | // Get the next bit to flip to produce the 8-bit Gray code at the provided index 258 | // Precondition: index > 0 and index < 256 259 | static int GetBitFlipForGrayCode8(int index) 260 | { 261 | if (index & 1) 262 | return 0; 263 | 264 | if (index & 15) 265 | return (0x6764 >> (index & 14)) & 3; 266 | 267 | return ((0x12131210 >> (index >> 3)) & 3) + 4; 268 | } 269 | 270 | static int GetBitFlipForGrayCode8_ref(int index) 271 | { 272 | int g0 = (index - 1) ^ ((index - 1) >> 1); 273 | int g1 = index ^ (index >> 1); 274 | int d = g1 ^ g0; 275 | unsigned long bit; 276 | if (1 != _BitScanForward(&bit, d)) 277 | { 278 | return 0; 279 | } 280 | return (int)bit; 281 | } 282 | 283 | static void ReflectedBinaryGrayCodeTest() 284 | { 285 | for (int index = 1; index < 256; ++index) 286 | { 287 | if (GetBitFlipForGrayCode8(index) != GetBitFlipForGrayCode8_ref(index)) 288 | { 289 | cout << "ERROR at " << index << " : " << GetBitFlipForGrayCode8(index) << " != " << GetBitFlipForGrayCode8_ref(index) << endl; 290 | } 291 | } 292 | 293 | int x = 0; 294 | 295 | siamese::PCGRandom prng; 296 | 297 | for (int i = 0; i < 10; ++i) 298 | { 299 | prng.Seed(0); 300 | 301 | ::Sleep(100); 302 | 303 | uint32_t t0, t1; 304 | 305 | t0 = (uint32_t)__rdtsc(); 306 | for (int trials = 0; trials < 10000; ++trials) 307 | { 308 | x ^= GetBitFlipForGrayCode8(prng.Next() % 255 + 1); 309 | } 310 | t1 = (uint32_t)__rdtsc(); 311 | cout << "New method: " << t1 - t0 << endl; 312 | 313 | prng.Seed(0); 314 | 315 | ::Sleep(100); 316 | 317 | t0 = (uint32_t)__rdtsc(); 318 | for (int trials = 0; trials < 10000; ++trials) 319 | { 320 | x ^= GetBitFlipForGrayCode8_ref(prng.Next() % 255 + 1); 321 | } 322 | t1 = (uint32_t)__rdtsc(); 323 | cout << "Old method: " << t1 - t0 << endl; 324 | } 325 | } 326 | 327 | #ifdef ENABLE_GENTAB_GRAYCODE 328 | 329 | int main() 330 | { 331 | //GenerateGrayCodes(); 332 | 333 | ReflectedBinaryGrayCodeTest(); 334 | 335 | return 0; 336 | } 337 | 338 | #endif // ENABLE_GENTAB_GRAYCODE 339 | -------------------------------------------------------------------------------- /tests/gentab_primes.cpp: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright (c) 2016 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 Siamese 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 | using namespace std; 33 | 34 | #include "../SiameseTools.h" 35 | #include "../gf256.h" 36 | 37 | #define ENABLE_GENTAB_PRIMES 38 | #define VERBOSE_PRIMES_TABLE_CREATION 39 | 40 | //#define SKIP_EVEN_PRIMES 41 | 42 | #ifdef VERBOSE_PRIMES_TABLE_CREATION 43 | #define PT_LOG(x) x 44 | #else 45 | #define PT_LOG(x) 46 | #endif 47 | 48 | static uint8_t int2gray(uint8_t num) 49 | { 50 | return num ^ (num >> 1); 51 | } 52 | 53 | /* 54 | Given a PRNG, generate a deck of cards in a random order. 55 | The deck will contain elements with values between 0 and count - 1. 56 | */ 57 | 58 | static void ShuffleDeck16(siamese::PCGRandom& prng, uint16_t* GF256_RESTRICT deck, uint32_t count) 59 | { 60 | deck[0] = 0; 61 | 62 | // If we can unroll 4 times, 63 | if (count <= 256) 64 | { 65 | for (uint32_t ii = 1;;) 66 | { 67 | uint32_t jj, rv = prng.Next(); 68 | 69 | // 8-bit unroll 70 | switch (count - ii) 71 | { 72 | default: 73 | jj = (uint8_t)rv % ii; 74 | deck[ii] = deck[jj]; 75 | deck[jj] = ii; 76 | ++ii; 77 | jj = (uint8_t)(rv >> 8) % ii; 78 | deck[ii] = deck[jj]; 79 | deck[jj] = ii; 80 | ++ii; 81 | jj = (uint8_t)(rv >> 16) % ii; 82 | deck[ii] = deck[jj]; 83 | deck[jj] = ii; 84 | ++ii; 85 | jj = (uint8_t)(rv >> 24) % ii; 86 | deck[ii] = deck[jj]; 87 | deck[jj] = ii; 88 | ++ii; 89 | break; 90 | 91 | case 3: 92 | jj = (uint8_t)rv % ii; 93 | deck[ii] = deck[jj]; 94 | deck[jj] = ii; 95 | ++ii; 96 | case 2: 97 | jj = (uint8_t)(rv >> 8) % ii; 98 | deck[ii] = deck[jj]; 99 | deck[jj] = ii; 100 | ++ii; 101 | case 1: 102 | jj = (uint8_t)(rv >> 16) % ii; 103 | deck[ii] = deck[jj]; 104 | deck[jj] = ii; 105 | case 0: 106 | return; 107 | } 108 | } 109 | } 110 | else 111 | { 112 | // For each deck entry, 113 | for (uint32_t ii = 1;;) 114 | { 115 | uint32_t jj, rv = prng.Next(); 116 | 117 | // 16-bit unroll 118 | switch (count - ii) 119 | { 120 | default: 121 | jj = (uint16_t)rv % ii; 122 | deck[ii] = deck[jj]; 123 | deck[jj] = ii; 124 | ++ii; 125 | jj = (uint16_t)(rv >> 16) % ii; 126 | deck[ii] = deck[jj]; 127 | deck[jj] = ii; 128 | ++ii; 129 | break; 130 | 131 | case 1: 132 | jj = (uint16_t)rv % ii; 133 | deck[ii] = deck[jj]; 134 | deck[jj] = ii; 135 | case 0: 136 | return; 137 | } 138 | } 139 | } 140 | } 141 | 142 | static void GeneratePrimesTable() 143 | { 144 | uint8_t Primes[256]; 145 | int PrimeCount = 0; 146 | 147 | for (int i = 0; i < 256; ++i) 148 | { 149 | uint8_t x = (uint8_t)i; 150 | 151 | #ifdef SKIP_EVEN_PRIMES 152 | // Skip even table entries, since they're similar to the odd ones 153 | if ((i & 1) == 0) 154 | { 155 | continue; 156 | } 157 | #endif 158 | 159 | PT_LOG(cout << endl << "Table for i = " << i << " : 01";) 160 | 161 | uint8_t table[12]; 162 | table[0] = 1; 163 | for (int j = 1; j < 12; ++j) 164 | { 165 | table[j] = gf256_mul(table[j - 1], x); 166 | PT_LOG(cout << " " << setw(2) << setfill('0') << hex << (int)table[j];) 167 | } 168 | 169 | PT_LOG(cout << endl;) 170 | #if 0 171 | if (x != table[0]) 172 | { 173 | cout << "Table entry 9 does not repeat entry 0!" << endl; 174 | assert(false); 175 | } 176 | #endif 177 | 178 | for (int j = 0; j < 12; ++j) 179 | { 180 | uint8_t y = table[j]; 181 | for (int k = j + 1; k < 12; ++k) 182 | { 183 | if (table[k] == y) 184 | { 185 | PT_LOG(cout << "Cycle between " << j << " and " << k << endl;) 186 | break; 187 | } 188 | } 189 | } 190 | 191 | uint8_t output[256]; 192 | PT_LOG(cout << endl << "Linear combinations :";) 193 | 194 | for (int j = 0; j < 256; ++j) 195 | { 196 | uint8_t z = 0; 197 | uint8_t g = int2gray((uint8_t)j); 198 | 199 | uint8_t mask = 1; 200 | for (int k = 0; k < 8; ++k) 201 | { 202 | if (0 != (g & mask)) 203 | { 204 | z ^= table[k]; 205 | } 206 | 207 | mask <<= 1; 208 | } 209 | 210 | output[j] = z; 211 | 212 | PT_LOG(cout << " " << setw(2) << setfill('0') << hex << (int)z;) 213 | } 214 | 215 | PT_LOG(cout << endl;) 216 | 217 | bool prime = true; 218 | for (int j = 0; j < 256; ++j) 219 | { 220 | uint8_t y = output[j]; 221 | for (int k = j + 1; k < 256; ++k) 222 | { 223 | if (output[k] == y) 224 | { 225 | PT_LOG(cout << "Early cycle between " << j << " and " << k << endl;) 226 | prime = false; 227 | goto done_checking_cycles; 228 | } 229 | } 230 | } 231 | done_checking_cycles:; 232 | 233 | if (prime) 234 | { 235 | Primes[PrimeCount++] = (uint8_t)i; 236 | 237 | cout << "Prime " << PrimeCount << " generator sequence: "; 238 | for (int j = 0; j < 12; ++j) 239 | { 240 | cout << " " << setw(2) << setfill('0') << hex << (int)table[j]; 241 | } 242 | cout << endl; 243 | } 244 | } 245 | 246 | cout << "static const int PrimesCount = " << dec << PrimeCount << ";" << endl; 247 | cout << "static const uint8_t Primes[PrimesCount] = {"; 248 | for (int i = 0; i < PrimeCount; ++i) 249 | { 250 | if (i % 16 == 0) 251 | { 252 | cout << endl << "\t"; 253 | } 254 | cout << "0x" << setw(2) << setfill('0') << hex << (int)Primes[i] << ", "; 255 | } 256 | cout << endl << "};" << endl; 257 | 258 | siamese::PCGRandom prng; 259 | prng.Seed(0); 260 | uint16_t indices[256]; 261 | ShuffleDeck16(prng, indices, PrimeCount); 262 | 263 | cout << "static const uint8_t ShuffledPrimes[PrimesCount] = {"; 264 | for (int i = 0; i < PrimeCount; ++i) 265 | { 266 | if (i % 16 == 0) 267 | { 268 | cout << endl << "\t"; 269 | } 270 | cout << "0x" << setw(2) << setfill('0') << hex << (int)Primes[indices[i]] << ", "; 271 | } 272 | cout << endl << "};" << endl; 273 | } 274 | 275 | 276 | #ifdef ENABLE_GENTAB_PRIMES 277 | 278 | int main() 279 | { 280 | if (0 != gf256_init()) 281 | { 282 | cout << "Failed to initialize gf256" << endl; 283 | return -1; 284 | } 285 | 286 | GeneratePrimesTable(); 287 | 288 | return 0; 289 | } 290 | 291 | #endif // ENABLE_GENTAB_PRIMES 292 | -------------------------------------------------------------------------------- /tests/msvc/SiameseInternalTests.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 | {E28D52C7-2A45-4E7E-86B5-75EA2F579C3E} 23 | SiameseInternalTests 24 | 10.0.17134.0 25 | 26 | 27 | 28 | Application 29 | true 30 | v141 31 | MultiByte 32 | 33 | 34 | Application 35 | true 36 | v141 37 | MultiByte 38 | 39 | 40 | Application 41 | false 42 | v141 43 | true 44 | MultiByte 45 | 46 | 47 | Application 48 | false 49 | v141 50 | true 51 | MultiByte 52 | 53 | 54 | 55 | 56 | 57 | 58 | 59 | 60 | 61 | 62 | 63 | 64 | 65 | 66 | 67 | 68 | 69 | 70 | Output/$(ProjectName)/$(Configuration)/$(Platform)/ 71 | Obj/$(ProjectName)/$(Configuration)/$(Platform)/ 72 | 73 | 74 | Output/$(ProjectName)/$(Configuration)/$(Platform)/ 75 | Obj/$(ProjectName)/$(Configuration)/$(Platform)/ 76 | 77 | 78 | Output/$(ProjectName)/$(Configuration)/$(Platform)/ 79 | Obj/$(ProjectName)/$(Configuration)/$(Platform)/ 80 | 81 | 82 | Output/$(ProjectName)/$(Configuration)/$(Platform)/ 83 | Obj/$(ProjectName)/$(Configuration)/$(Platform)/ 84 | 85 | 86 | 87 | Level3 88 | Disabled 89 | true 90 | MultiThreadedDebug 91 | 92 | 93 | true 94 | 95 | 96 | 97 | 98 | Level3 99 | Disabled 100 | true 101 | MultiThreadedDebug 102 | 103 | 104 | true 105 | 106 | 107 | 108 | 109 | Level3 110 | MaxSpeed 111 | true 112 | true 113 | true 114 | MultiThreaded 115 | 116 | 117 | true 118 | true 119 | true 120 | 121 | 122 | 123 | 124 | Level3 125 | MaxSpeed 126 | true 127 | true 128 | true 129 | MultiThreaded 130 | 131 | 132 | true 133 | true 134 | true 135 | 136 | 137 | 138 | 139 | 140 | 141 | 142 | 143 | 144 | 145 | 146 | {ff5912ef-7424-4974-b877-62b03d5046c6} 147 | 148 | 149 | 150 | 151 | 152 | -------------------------------------------------------------------------------- /tests/msvc/SiameseInternalTests.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 | {2f1bac03-87e6-4de5-a56d-14bfe5b87a6e} 18 | 19 | 20 | 21 | 22 | Source Files\tests 23 | 24 | 25 | Source Files\tests 26 | 27 | 28 | 29 | 30 | Source Files\tests 31 | 32 | 33 | -------------------------------------------------------------------------------- /tests/msvc/SiameseUnitTest.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 | {32176592-2F30-4BD5-B645-EB11C8D3453E} 23 | Siamese 24 | SiameseUnitTests 25 | 10.0.17134.0 26 | 27 | 28 | 29 | Application 30 | true 31 | MultiByte 32 | v141 33 | 34 | 35 | Application 36 | true 37 | MultiByte 38 | v141 39 | 40 | 41 | Application 42 | false 43 | true 44 | MultiByte 45 | v141 46 | 47 | 48 | Application 49 | false 50 | true 51 | MultiByte 52 | v141 53 | 54 | 55 | 56 | 57 | 58 | 59 | 60 | 61 | 62 | 63 | 64 | 65 | 66 | 67 | 68 | 69 | 70 | 71 | Output/$(ProjectName)/$(Configuration)/$(Platform)/ 72 | Obj/$(ProjectName)/$(Configuration)/$(Platform)/ 73 | 74 | 75 | Output/$(ProjectName)/$(Configuration)/$(Platform)/ 76 | Obj/$(ProjectName)/$(Configuration)/$(Platform)/ 77 | 78 | 79 | Output/$(ProjectName)/$(Configuration)/$(Platform)/ 80 | Obj/$(ProjectName)/$(Configuration)/$(Platform)/ 81 | 82 | 83 | Output/$(ProjectName)/$(Configuration)/$(Platform)/ 84 | Obj/$(ProjectName)/$(Configuration)/$(Platform)/ 85 | 86 | 87 | 88 | Level3 89 | Disabled 90 | true 91 | MultiThreadedDebug 92 | _MBCS;%(PreprocessorDefinitions) 93 | 94 | 95 | true 96 | 97 | 98 | 99 | 100 | 101 | 102 | 103 | 104 | 105 | 106 | Level3 107 | Disabled 108 | true 109 | MultiThreadedDebug 110 | _MBCS;%(PreprocessorDefinitions) 111 | 112 | 113 | true 114 | 115 | 116 | 117 | 118 | 119 | 120 | 121 | 122 | 123 | 124 | Level3 125 | MaxSpeed 126 | true 127 | true 128 | true 129 | AnySuitable 130 | Speed 131 | false 132 | MultiThreaded 133 | true 134 | _MBCS;%(PreprocessorDefinitions) 135 | 136 | 137 | true 138 | true 139 | true 140 | 141 | 142 | 143 | 144 | 145 | 146 | 147 | 148 | 149 | 150 | Level3 151 | MaxSpeed 152 | true 153 | true 154 | true 155 | AnySuitable 156 | Speed 157 | false 158 | MultiThreaded 159 | true 160 | _MBCS;%(PreprocessorDefinitions) 161 | 162 | 163 | true 164 | true 165 | true 166 | 167 | 168 | 169 | 170 | 171 | 172 | 173 | 174 | 175 | 176 | 177 | 178 | 179 | 180 | 181 | 182 | {ff5912ef-7424-4974-b877-62b03d5046c6} 183 | 184 | 185 | 186 | 187 | 188 | -------------------------------------------------------------------------------- /tests/msvc/SiameseUnitTest.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 | {afbac4c8-5d76-4c40-827a-3c3a71550073} 18 | 19 | 20 | 21 | 22 | Source Files 23 | 24 | 25 | 26 | 27 | docs 28 | 29 | 30 | -------------------------------------------------------------------------------- /tests/test_allocator.cpp: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright (c) 2016 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 Siamese 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 | using namespace std; 34 | 35 | #include "../PacketAllocator.h" 36 | #include "../SiameseTools.h" 37 | 38 | #define ENABLE_TEST_ALLOCATOR 39 | #define ENABLE_TEST_REALLOC 40 | #define ENABLE_TEST_SHRINK 41 | 42 | static const unsigned kAllocationCount = 8000; 43 | 44 | void TestAllocator() 45 | { 46 | pktalloc::Allocator allocator; 47 | 48 | cout << "Used at start: " << allocator.GetMemoryUsedBytes() << endl; 49 | cout << "Allocated at start: " << allocator.GetMemoryAllocatedBytes() << endl; 50 | 51 | siamese::PCGRandom prng; 52 | prng.Seed(0); 53 | 54 | for (unsigned j = 0; j < 2000; ++j) 55 | { 56 | cout << "Test iteration " << j << endl; 57 | 58 | if (!allocator.IntegrityCheck()) 59 | { 60 | cout << "Integrity check failed(3)!" << endl; 61 | return; 62 | } 63 | 64 | #ifdef ENABLE_TEST_SHRINK 65 | { 66 | uint8_t* allocationsA[kAllocationCount]; 67 | unsigned sizesA[kAllocationCount]; 68 | unsigned sizesB[kAllocationCount]; 69 | 70 | for (unsigned i = 0; i < kAllocationCount; ++i) 71 | { 72 | sizesA[i] = 1 + (prng.Next() % 4000); 73 | sizesB[i] = sizesA[i] + (prng.Next() % (4000 - sizesA[i] + 1)); 74 | SIAMESE_DEBUG_ASSERT(sizesB[i] >= sizesA[i]); 75 | allocationsA[i] = allocator.Allocate(sizesB[i]); 76 | SIAMESE_DEBUG_ASSERT(allocationsA[i]); 77 | memset(allocationsA[i], 1, sizesB[i]); 78 | } 79 | 80 | for (unsigned i = 0; i < kAllocationCount; ++i) 81 | { 82 | allocator.Shrink(allocationsA[i], sizesA[i]); 83 | memset(allocationsA[i], 3, sizesA[i]); 84 | } 85 | 86 | for (unsigned i = 0; i < kAllocationCount; ++i) 87 | { 88 | memset(allocationsA[i], 4, sizesA[i]); 89 | allocator.Free(allocationsA[i]); 90 | } 91 | } 92 | 93 | if (!allocator.IntegrityCheck()) 94 | { 95 | cout << "Integrity check failed(1)!" << endl; 96 | return; 97 | } 98 | #endif 99 | 100 | { 101 | uint8_t* allocationsA[kAllocationCount]; 102 | unsigned sizesA[kAllocationCount]; 103 | 104 | for (unsigned i = 0; i < kAllocationCount; ++i) 105 | { 106 | sizesA[i] = i + 1; 107 | allocationsA[i] = allocator.Allocate(sizesA[i]); 108 | SIAMESE_DEBUG_ASSERT(allocationsA[i]); 109 | memset(allocationsA[i], 1, sizesA[i]); 110 | } 111 | 112 | for (unsigned i = 0; i < kAllocationCount; ++i) 113 | { 114 | memset(allocationsA[i], 2, sizesA[i]); 115 | allocator.Free(allocationsA[i]); 116 | } 117 | 118 | uint8_t* allocationsB[kAllocationCount]; 119 | unsigned sizesB[kAllocationCount]; 120 | 121 | for (unsigned i = 0; i < kAllocationCount; ++i) 122 | { 123 | sizesB[i] = 1 + (prng.Next() % 4000); 124 | allocationsB[i] = allocator.Allocate(sizesB[i]); 125 | SIAMESE_DEBUG_ASSERT(allocationsB[i]); 126 | memset(allocationsB[i], 1, sizesB[i]); 127 | } 128 | 129 | for (unsigned i = 0; i < kAllocationCount; ++i) 130 | { 131 | memset(allocationsB[i], 2, sizesB[i]); 132 | allocator.Free(allocationsB[i]); 133 | } 134 | } 135 | 136 | if (!allocator.IntegrityCheck()) 137 | { 138 | cout << "Integrity check failed(1)!" << endl; 139 | return; 140 | } 141 | 142 | { 143 | uint8_t* allocationsA[kAllocationCount]; 144 | uint8_t* allocationsB[kAllocationCount]; 145 | unsigned sizesA[kAllocationCount]; 146 | unsigned sizesB[kAllocationCount]; 147 | 148 | for (unsigned i = 0; i < kAllocationCount; ++i) 149 | { 150 | sizesA[i] = i + 1; 151 | allocationsA[i] = allocator.Allocate(sizesA[i]); 152 | SIAMESE_DEBUG_ASSERT(allocationsA[i]); 153 | memset(allocationsA[i], 1, sizesA[i]); 154 | 155 | sizesB[i] = 1 + (prng.Next() % 4000); 156 | allocationsB[i] = allocator.Allocate(sizesB[i]); 157 | SIAMESE_DEBUG_ASSERT(allocationsB[i]); 158 | memset(allocationsB[i], 1, sizesB[i]); 159 | 160 | memset(allocationsA[i], 2, sizesA[i]); 161 | allocator.Free(allocationsA[i]); 162 | } 163 | 164 | for (unsigned i = 0; i < kAllocationCount; ++i) 165 | { 166 | memset(allocationsB[i], 2, sizesB[i]); 167 | allocator.Free(allocationsB[i]); 168 | } 169 | } 170 | 171 | if (!allocator.IntegrityCheck()) 172 | { 173 | cout << "Integrity check failed(2)!" << endl; 174 | return; 175 | } 176 | 177 | { 178 | uint8_t* allocationsA[kAllocationCount]; 179 | uint8_t* allocationsB[kAllocationCount]; 180 | unsigned sizesA[kAllocationCount]; 181 | unsigned sizesB[kAllocationCount]; 182 | 183 | for (unsigned i = 0; i < kAllocationCount; ++i) 184 | { 185 | sizesB[i] = 1 + (prng.Next() % 4000); 186 | allocationsB[i] = allocator.Allocate(sizesB[i]); 187 | SIAMESE_DEBUG_ASSERT(allocationsB[i]); 188 | memset(allocationsB[i], 1, sizesB[i]); 189 | 190 | sizesA[i] = i + 1; 191 | allocationsA[i] = allocator.Allocate(sizesA[i]); 192 | SIAMESE_DEBUG_ASSERT(allocationsA[i]); 193 | memset(allocationsA[i], 1, sizesA[i]); 194 | 195 | memset(allocationsB[i], 2, sizesB[i]); 196 | allocator.Free(allocationsB[i]); 197 | } 198 | 199 | for (unsigned i = 0; i < kAllocationCount; ++i) 200 | { 201 | memset(allocationsA[i], 2, sizesA[i]); 202 | allocator.Free(allocationsA[i]); 203 | } 204 | } 205 | 206 | if (!allocator.IntegrityCheck()) 207 | { 208 | cout << "Integrity check failed(4)!" << endl; 209 | return; 210 | } 211 | 212 | #ifdef ENABLE_TEST_REALLOC 213 | { 214 | uint8_t* allocationsA[kAllocationCount]; 215 | unsigned sizesA[kAllocationCount]; 216 | uint8_t* allocationsB[kAllocationCount]; 217 | unsigned sizesB[kAllocationCount]; 218 | 219 | for (unsigned i = 0; i < kAllocationCount; ++i) 220 | { 221 | sizesA[i] = 1 + (prng.Next() % 4000); 222 | allocationsA[i] = allocator.Allocate(sizesA[i]); 223 | SIAMESE_DEBUG_ASSERT(allocationsA[i]); 224 | memset(allocationsA[i], 1, sizesA[i]); 225 | } 226 | 227 | for (unsigned i = 0; i < kAllocationCount; ++i) 228 | { 229 | sizesB[i] = 1 + (prng.Next() % 4000); 230 | allocationsB[i] = allocator.Reallocate(allocationsA[i], sizesB[i], pktalloc::Realloc::Uninitialized); 231 | memset(allocationsB[i], 3, sizesB[i]); 232 | } 233 | 234 | for (unsigned i = 0; i < kAllocationCount; ++i) 235 | { 236 | memset(allocationsB[i], 4, sizesB[i]); 237 | allocator.Free(allocationsB[i]); 238 | } 239 | } 240 | 241 | if (!allocator.IntegrityCheck()) 242 | { 243 | cout << "Integrity check failed(1)!" << endl; 244 | return; 245 | } 246 | #endif 247 | } 248 | 249 | cout << "Used at end: " << allocator.GetMemoryUsedBytes() << endl; 250 | cout << "Allocated at end: " << allocator.GetMemoryAllocatedBytes() << endl; 251 | } 252 | 253 | #ifdef ENABLE_TEST_ALLOCATOR 254 | 255 | int main() 256 | { 257 | TestAllocator(); 258 | 259 | int x; 260 | cin >> x; 261 | 262 | return 0; 263 | } 264 | 265 | #endif // ENABLE_UNIT_TEST 266 | -------------------------------------------------------------------------------- /tests/test_custom_bitset.cpp: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright (c) 2016 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 Siamese 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 | using namespace std; 34 | 35 | #include "../SiameseDecoder.h" 36 | 37 | #define ENABLE_TEST_BITFIELD 38 | 39 | inline void Assert(unsigned N, bool condition) 40 | { 41 | if (!condition) 42 | { 43 | cout << "**************************** FAILED for N = " << N << endl; 44 | SIAMESE_DEBUG_BREAK(); 45 | } 46 | } 47 | 48 | template 49 | void TestBitfield() 50 | { 51 | pktalloc::CustomBitSet bf; 52 | 53 | // Set/Clear/Check: 54 | cout << "Testing Set/Clear/Check for N = " << BFSize << endl; 55 | 56 | bf.ClearAll(); 57 | for (unsigned j = 0; j < BFSize; ++j) 58 | { 59 | Assert(BFSize, !bf.Check(j)); 60 | } 61 | 62 | for (unsigned i = 0; i < BFSize; ++i) 63 | { 64 | bf.ClearAll(); 65 | bf.Set(i); 66 | for (unsigned j = 0; j < BFSize; ++j) 67 | { 68 | Assert(BFSize, bf.Check(j) == (j == i)); 69 | } 70 | } 71 | 72 | for (unsigned i = 0; i < BFSize; ++i) 73 | { 74 | bf.SetAll(); 75 | bf.Clear(i); 76 | for (unsigned j = 0; j < BFSize; ++j) 77 | { 78 | Assert(BFSize, bf.Check(j) == (j != i)); 79 | } 80 | } 81 | 82 | for (unsigned i = 0; i < BFSize; ++i) 83 | { 84 | bf.Set(i); 85 | } 86 | for (unsigned j = 0; j < BFSize; ++j) 87 | { 88 | Assert(BFSize, bf.Check(j)); 89 | } 90 | 91 | // SetRange: 92 | cout << "Testing SetRange/ClearRange for N = " << BFSize << endl; 93 | 94 | for (unsigned i = 0; i < BFSize; ++i) 95 | { 96 | for (unsigned j = i; j <= BFSize; ++j) 97 | { 98 | bf.ClearAll(); 99 | bf.SetRange(i, j); 100 | for (unsigned k = 0; k < BFSize; ++k) 101 | { 102 | Assert(BFSize, bf.Check(k) == (k >= i && k < j)); 103 | } 104 | } 105 | } 106 | 107 | // ClearRange: 108 | 109 | for (unsigned i = 0; i < BFSize; ++i) 110 | { 111 | for (unsigned j = i; j <= BFSize; ++j) 112 | { 113 | bf.SetAll(); 114 | bf.ClearRange(i, j); 115 | for (unsigned k = 0; k < BFSize; ++k) 116 | { 117 | Assert(BFSize, bf.Check(k) != (k >= i && k < j)); 118 | } 119 | } 120 | } 121 | 122 | // RangePopcount: 123 | 124 | static const unsigned kRandomTrials = 1000; 125 | for (unsigned i = 0; i < kRandomTrials; ++i) 126 | { 127 | if (i % 100 == 0) 128 | { 129 | cout << "Testing RangePopcount for N = " << BFSize << "..." << endl; 130 | } 131 | 132 | bf.ClearAll(); 133 | if (i == 0) 134 | { 135 | // Empty 136 | } 137 | else if (i == 1) 138 | { 139 | // All on 140 | for (unsigned j = 0; j < BFSize; ++j) 141 | { 142 | bf.Set(j); 143 | } 144 | } 145 | else if (i == 2) 146 | { 147 | // Evens 148 | for (unsigned j = 0; j < BFSize; j += 2) 149 | { 150 | bf.Set(j); 151 | } 152 | } 153 | else if (i == 3) 154 | { 155 | // Odds 156 | for (unsigned j = 1; j < BFSize; j += 2) 157 | { 158 | bf.Set(j); 159 | } 160 | } 161 | else 162 | { 163 | siamese::PCGRandom prng; 164 | prng.Seed(i + BFSize * kRandomTrials); 165 | 166 | bf.ClearAll(); 167 | for (unsigned j = 0; j < BFSize; ++j) 168 | { 169 | if ((prng.Next() & 4) != 0) 170 | bf.Set(j); 171 | } 172 | } 173 | 174 | for (unsigned j = 0; j < BFSize; ++j) 175 | { 176 | for (unsigned k = j + 1; k < BFSize; ++k) 177 | { 178 | unsigned count = bf.RangePopcount(j, k); 179 | 180 | unsigned refCount = 0; 181 | for (unsigned t = j; t < k; ++t) 182 | { 183 | if (bf.Check(t)) 184 | refCount++; 185 | } 186 | 187 | Assert(BFSize, count == refCount); 188 | } 189 | } 190 | } 191 | 192 | // FindFirstClear: 193 | cout << "Testing FindFirstClear for N = " << BFSize << endl; 194 | 195 | for (unsigned i = 0; i < BFSize; ++i) 196 | { 197 | bf.ClearAll(); 198 | for (unsigned j = 0; j < BFSize; ++j) 199 | { 200 | if (i != j) 201 | bf.Set(j); 202 | } 203 | 204 | for (unsigned j = 0; j < BFSize; ++j) 205 | { 206 | if (j <= i) 207 | { 208 | Assert(BFSize, i == bf.FindFirstClear(j)); 209 | } 210 | else 211 | { 212 | Assert(BFSize, bf.kValidBits == bf.FindFirstClear(j)); 213 | } 214 | } 215 | } 216 | 217 | for (unsigned i = 0; i < BFSize; ++i) 218 | { 219 | bf.ClearAll(); 220 | for (unsigned j = 0; j < BFSize; ++j) 221 | { 222 | if (j >= i) 223 | bf.Set(j); 224 | } 225 | 226 | for (unsigned j = 0; j < BFSize; ++j) 227 | { 228 | if (j < i) 229 | { 230 | Assert(BFSize, j == bf.FindFirstClear(j)); 231 | } 232 | else 233 | { 234 | Assert(BFSize, bf.kValidBits == bf.FindFirstClear(j)); 235 | } 236 | } 237 | } 238 | 239 | for (unsigned i = 0; i < BFSize; ++i) 240 | { 241 | bf.ClearAll(); 242 | for (unsigned j = 0; j < BFSize; ++j) 243 | { 244 | if (j < i) 245 | bf.Set(j); 246 | } 247 | 248 | for (unsigned j = 0; j < BFSize; ++j) 249 | { 250 | if (j < i) 251 | { 252 | Assert(BFSize, i == bf.FindFirstClear(j)); 253 | } 254 | else 255 | { 256 | Assert(BFSize, j == bf.FindFirstClear(j)); 257 | } 258 | } 259 | } 260 | 261 | // FindFirstSet: 262 | cout << "Testing FindFirstSet for N = " << BFSize << endl; 263 | 264 | for (unsigned i = 0; i < BFSize; ++i) 265 | { 266 | bf.SetAll(); 267 | for (unsigned j = 0; j < BFSize; ++j) 268 | { 269 | if (i != j) 270 | bf.Clear(j); 271 | } 272 | 273 | for (unsigned j = 0; j < BFSize; ++j) 274 | { 275 | if (j <= i) 276 | { 277 | Assert(BFSize, i == bf.FindFirstSet(j)); 278 | } 279 | else 280 | { 281 | Assert(BFSize, bf.kValidBits == bf.FindFirstSet(j)); 282 | } 283 | } 284 | } 285 | 286 | for (unsigned i = 0; i < BFSize; ++i) 287 | { 288 | bf.SetAll(); 289 | for (unsigned j = 0; j < BFSize; ++j) 290 | { 291 | if (j >= i) 292 | bf.Clear(j); 293 | } 294 | 295 | for (unsigned j = 0; j < BFSize; ++j) 296 | { 297 | if (j < i) 298 | { 299 | Assert(BFSize, j == bf.FindFirstSet(j)); 300 | } 301 | else 302 | { 303 | Assert(BFSize, bf.kValidBits == bf.FindFirstSet(j)); 304 | } 305 | } 306 | } 307 | 308 | for (unsigned i = 0; i < BFSize; ++i) 309 | { 310 | bf.SetAll(); 311 | for (unsigned j = 0; j < BFSize; ++j) 312 | { 313 | if (j < i) 314 | bf.Clear(j); 315 | } 316 | 317 | for (unsigned j = 0; j < BFSize; ++j) 318 | { 319 | if (j < i) 320 | { 321 | Assert(BFSize, i == bf.FindFirstSet(j)); 322 | } 323 | else 324 | { 325 | Assert(BFSize, j == bf.FindFirstSet(j)); 326 | } 327 | } 328 | } 329 | } 330 | 331 | 332 | #ifdef ENABLE_TEST_BITFIELD 333 | 334 | int main() 335 | { 336 | TestBitfield<1>(); 337 | TestBitfield<2>(); 338 | TestBitfield<3>(); 339 | TestBitfield<4>(); 340 | TestBitfield<5>(); 341 | TestBitfield<6>(); 342 | TestBitfield<7>(); 343 | TestBitfield<8>(); 344 | TestBitfield<9>(); 345 | TestBitfield<10>(); 346 | TestBitfield<11>(); 347 | TestBitfield<12>(); 348 | TestBitfield<13>(); 349 | TestBitfield<14>(); 350 | TestBitfield<15>(); 351 | TestBitfield<16>(); 352 | TestBitfield<17>(); 353 | TestBitfield<18>(); 354 | TestBitfield<19>(); 355 | TestBitfield<20>(); 356 | TestBitfield<21>(); 357 | TestBitfield<22>(); 358 | TestBitfield<23>(); 359 | TestBitfield<24>(); 360 | TestBitfield<25>(); 361 | TestBitfield<26>(); 362 | TestBitfield<27>(); 363 | TestBitfield<28>(); 364 | TestBitfield<29>(); 365 | TestBitfield<30>(); 366 | TestBitfield<31>(); 367 | TestBitfield<32>(); 368 | TestBitfield<33>(); 369 | TestBitfield<34>(); 370 | TestBitfield<35>(); 371 | TestBitfield<36>(); 372 | TestBitfield<37>(); 373 | TestBitfield<38>(); 374 | TestBitfield<39>(); 375 | TestBitfield<40>(); 376 | TestBitfield<41>(); 377 | TestBitfield<42>(); 378 | TestBitfield<43>(); 379 | TestBitfield<44>(); 380 | TestBitfield<45>(); 381 | TestBitfield<46>(); 382 | TestBitfield<47>(); 383 | TestBitfield<48>(); 384 | TestBitfield<49>(); 385 | TestBitfield<50>(); 386 | TestBitfield<51>(); 387 | TestBitfield<52>(); 388 | TestBitfield<53>(); 389 | TestBitfield<54>(); 390 | TestBitfield<55>(); 391 | TestBitfield<56>(); 392 | TestBitfield<57>(); 393 | TestBitfield<58>(); 394 | TestBitfield<59>(); 395 | TestBitfield<60>(); 396 | TestBitfield<61>(); 397 | TestBitfield<62>(); 398 | TestBitfield<63>(); 399 | TestBitfield<64>(); 400 | TestBitfield<65>(); 401 | TestBitfield<66>(); 402 | TestBitfield<67>(); 403 | TestBitfield<68>(); 404 | TestBitfield<69>(); 405 | 406 | TestBitfield<126>(); 407 | TestBitfield<127>(); 408 | TestBitfield<128>(); 409 | TestBitfield<129>(); 410 | TestBitfield<130>(); 411 | 412 | TestBitfield<254>(); 413 | TestBitfield<255>(); 414 | TestBitfield<256>(); 415 | TestBitfield<257>(); 416 | TestBitfield<258>(); 417 | 418 | TestBitfield<1022>(); 419 | TestBitfield<1023>(); 420 | TestBitfield<1024>(); 421 | TestBitfield<1025>(); 422 | TestBitfield<1026>(); 423 | 424 | TestBitfield<1235>(); 425 | 426 | return 0; 427 | } 428 | 429 | #endif // ENABLE_UNIT_TEST 430 | -------------------------------------------------------------------------------- /tests/test_recovery_sort.cpp: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright (c) 2016 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 Siamese 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 | #define ENABLE_TEST_RECOVERY_SORT 30 | 31 | #include 32 | #include 33 | #include 34 | #include 35 | using namespace std; 36 | 37 | #include "../SiameseDecoder.h" 38 | #include "../SiameseCommon.h" 39 | 40 | #ifdef ENABLE_TEST_RECOVERY_SORT 41 | #include "../SiameseDecoder.cpp" 42 | #include "../SiameseCommon.cpp" 43 | #endif 44 | 45 | static void TestRecoverySort() 46 | { 47 | // Verifying assertion: 48 | /* 49 | This insertion order guarantees that the left and right side of 50 | the recovery input ranges are monotonically increasing as in: 51 | 52 | recovery 0: 012345 53 | recovery 1: 23456 <- Cauchy row 54 | recovery 2: 01234567 55 | recovery 3: 45678 56 | recovery 4: 456789 57 | */ 58 | pktalloc::Allocator allocator; 59 | siamese::RecoveryPacketList lister; 60 | siamese::CheckedRegionState region; 61 | lister.TheAllocator = &allocator; 62 | lister.CheckedRegion = ®ion; 63 | siamese::RecoveryMatrixState matrix; 64 | region.RecoveryMatrix = &matrix; 65 | 66 | static const unsigned kRecoveryCount = 5; 67 | static const unsigned kRanges[kRecoveryCount * 2] = { 68 | 0, 5, 69 | 2, 6, 70 | 0, 7, 71 | 4, 8, 72 | 4, 9 73 | }; 74 | siamese::RecoveryPacket* recoveries[kRecoveryCount]; 75 | 76 | // Check all possible inputs 77 | unsigned order[kRecoveryCount]; 78 | for (unsigned i = 0; i < kRecoveryCount; ++i) 79 | { 80 | order[0] = i; 81 | for (unsigned j = 0; j < kRecoveryCount; ++j) 82 | { 83 | if (i == j) 84 | continue; 85 | order[1] = j; 86 | for (unsigned k = 0; k < kRecoveryCount; ++k) 87 | { 88 | if (i == k || j == k) 89 | continue; 90 | order[2] = k; 91 | for (unsigned m = 0; m < kRecoveryCount; ++m) 92 | { 93 | if (i == m || j == m || k == m) 94 | continue; 95 | order[3] = m; 96 | for (unsigned n = 0; n < kRecoveryCount; ++n) 97 | { 98 | if (i == n || j == n || k == n || m == n) 99 | continue; 100 | order[4] = n; 101 | static_assert(5 == kRecoveryCount, "FIX"); 102 | 103 | for (unsigned y = 0; y < kRecoveryCount; ++y) 104 | { 105 | siamese::RecoveryPacket* recovery = allocator.Construct(); 106 | recovery->Metadata.ColumnStart = kRanges[y * 2]; 107 | recovery->Metadata.SumCount = kRanges[y * 2 + 1] - kRanges[y * 2] + 1; 108 | 109 | recovery->Metadata.Row = y; 110 | recovery->ElementStart = recovery->Metadata.ColumnStart; 111 | recovery->Metadata.LDPCCount = recovery->Metadata.SumCount; 112 | recovery->ElementEnd = recovery->ElementStart + recovery->Metadata.SumCount; 113 | 114 | unsigned x = order[y]; 115 | recoveries[x] = recovery; 116 | lister.Insert(recovery); 117 | } 118 | 119 | SIAMESE_DEBUG_ASSERT(lister.RecoveryPacketCount == kRecoveryCount); 120 | SIAMESE_DEBUG_ASSERT(lister.Head); 121 | SIAMESE_DEBUG_ASSERT(lister.Tail); 122 | 123 | siamese::RecoveryPacket* recovery = lister.Head; 124 | siamese::RecoveryPacket* prev = nullptr; 125 | for (unsigned y = 0; y < kRecoveryCount; ++y) 126 | { 127 | SIAMESE_DEBUG_ASSERT(recovery != nullptr); 128 | // Verify it was sorted in the correct order 129 | SIAMESE_DEBUG_ASSERT(recovery->Metadata.Row == y); 130 | prev = recovery; 131 | recovery = recovery->Next; 132 | SIAMESE_DEBUG_ASSERT(!recovery || recovery->Prev == prev); 133 | lister.Delete(prev); 134 | } 135 | SIAMESE_DEBUG_ASSERT(recovery == nullptr); 136 | 137 | const unsigned usedBytes = allocator.GetMemoryUsedBytes(); 138 | SIAMESE_DEBUG_ASSERT(usedBytes == 0); 139 | SIAMESE_DEBUG_ASSERT(lister.RecoveryPacketCount == 0); 140 | } 141 | } 142 | } 143 | } 144 | } 145 | 146 | cout << "Test passed!" << endl; 147 | } 148 | 149 | #ifdef ENABLE_TEST_RECOVERY_SORT 150 | 151 | int main() 152 | { 153 | TestRecoverySort(); 154 | 155 | int x; 156 | cin >> x; 157 | 158 | return 0; 159 | } 160 | 161 | #endif 162 | -------------------------------------------------------------------------------- /tests/test_serializers.cpp: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright (c) 2016 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 Siamese 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 | using namespace std; 34 | 35 | #include "../siamese.h" 36 | #include "../SiameseCommon.h" 37 | #include "../SiameseSerializers.h" 38 | 39 | #define ENABLE_TEST_SERIALIZERS 40 | 41 | inline void Assert(unsigned N, bool condition) 42 | { 43 | if (!condition) 44 | { 45 | cout << "**************************** FAILED for N = " << N << endl; 46 | SIAMESE_DEBUG_BREAK(); 47 | } 48 | } 49 | 50 | bool TestPODSerialization16() 51 | { 52 | static const unsigned kBufferBytes = 20; 53 | uint8_t buffer[kBufferBytes] = { 0 }; 54 | 55 | uint16_t x = 0xabcd; 56 | 57 | siamese::WriteU16_LE(buffer + 1, x); 58 | 59 | const uint8_t expectedResult[kBufferBytes] = { 60 | 0, 0xcd, 0xab, 0 61 | }; 62 | 63 | for (unsigned i = 0; i < 20; ++i) 64 | { 65 | if (buffer[i] != expectedResult[i]) 66 | { 67 | SIAMESE_DEBUG_BREAK(); 68 | return false; 69 | } 70 | } 71 | 72 | buffer[0] = 0xff; 73 | buffer[3] = 0xff; 74 | 75 | if (siamese::ReadU16_LE(buffer + 1) != x) 76 | { 77 | SIAMESE_DEBUG_BREAK(); 78 | return false; 79 | } 80 | 81 | return true; 82 | } 83 | 84 | bool TestPODSerialization24() 85 | { 86 | static const unsigned kBufferBytes = 20; 87 | uint8_t buffer[kBufferBytes] = { 0 }; 88 | 89 | uint32_t x = 0xabcdef; 90 | 91 | siamese::WriteU24_LE(buffer + 1, x); 92 | 93 | const uint8_t expectedResult[kBufferBytes] = { 94 | 0, 0xef, 0xcd, 0xab, 0 95 | }; 96 | 97 | for (unsigned i = 0; i < 20; ++i) 98 | { 99 | if (buffer[i] != expectedResult[i]) 100 | { 101 | SIAMESE_DEBUG_BREAK(); 102 | return false; 103 | } 104 | } 105 | 106 | buffer[0] = 0xff; 107 | buffer[4] = 0xff; 108 | 109 | if (siamese::ReadU24_LE(buffer + 1) != x) 110 | { 111 | SIAMESE_DEBUG_BREAK(); 112 | return false; 113 | } 114 | 115 | return true; 116 | } 117 | 118 | bool TestPODSerialization32() 119 | { 120 | static const unsigned kBufferBytes = 20; 121 | uint8_t buffer[kBufferBytes] = { 0 }; 122 | 123 | uint32_t x = 0x89abcdef; 124 | 125 | siamese::WriteU32_LE(buffer + 1, x); 126 | 127 | const uint8_t expectedResult[kBufferBytes] = { 128 | 0, 0xef, 0xcd, 0xab, 0x89, 0 129 | }; 130 | 131 | for (unsigned i = 0; i < 20; ++i) 132 | { 133 | if (buffer[i] != expectedResult[i]) 134 | { 135 | SIAMESE_DEBUG_BREAK(); 136 | return false; 137 | } 138 | } 139 | 140 | buffer[0] = 0xff; 141 | buffer[5] = 0xff; 142 | 143 | if (siamese::ReadU32_LE(buffer + 1) != x) 144 | { 145 | SIAMESE_DEBUG_BREAK(); 146 | return false; 147 | } 148 | 149 | return true; 150 | } 151 | 152 | bool TestPODSerialization64() 153 | { 154 | static const unsigned kBufferBytes = 20; 155 | uint8_t buffer[kBufferBytes] = { 0 }; 156 | 157 | uint64_t x = 0x0123456789abcdefULL; 158 | 159 | siamese::WriteU64_LE(buffer + 1, x); 160 | 161 | const uint8_t expectedResult[kBufferBytes] = { 162 | 0, 0xef, 0xcd, 0xab, 0x89, 0x67, 0x45, 0x23, 0x01, 0 163 | }; 164 | 165 | for (unsigned i = 0; i < 20; ++i) 166 | { 167 | if (buffer[i] != expectedResult[i]) 168 | { 169 | SIAMESE_DEBUG_BREAK(); 170 | return false; 171 | } 172 | } 173 | 174 | buffer[0] = 0xff; 175 | buffer[9] = 0xff; 176 | 177 | if (siamese::ReadU64_LE(buffer + 1) != x) 178 | { 179 | SIAMESE_DEBUG_BREAK(); 180 | return false; 181 | } 182 | 183 | return true; 184 | } 185 | 186 | bool TestByteStream() 187 | { 188 | uint8_t buffer[256]; 189 | 190 | siamese::WriteByteStream bs(buffer, 256); 191 | 192 | const uint8_t x = 0x01; 193 | const uint16_t y = 0x2345; 194 | const uint32_t z = 0x6789ab; 195 | const uint32_t w = 0xcdef1234; 196 | const uint64_t t = 0x2143567890badcfeULL; 197 | 198 | bs.Write8(x); 199 | bs.Write16(y); 200 | bs.Write24(z); 201 | bs.Write32(w); 202 | bs.Write64(t); 203 | 204 | if (bs.WrittenBytes != 1 + 2 + 3 + 4 + 8) 205 | { 206 | SIAMESE_DEBUG_BREAK(); 207 | return false; 208 | } 209 | 210 | siamese::ReadByteStream bs1(buffer, 256); 211 | 212 | uint8_t x1 = bs1.Read8(); 213 | uint16_t y1 = bs1.Read16(); 214 | uint32_t z1 = bs1.Read24(); 215 | uint32_t w1 = bs1.Read32(); 216 | uint64_t t1 = bs1.Read64(); 217 | 218 | if (x != x1 || y != y1 || z != z1 || w != w1 || t != t1) 219 | { 220 | return false; 221 | } 222 | if (bs1.BytesRead != 1 + 2 + 3 + 4 + 8) 223 | { 224 | SIAMESE_DEBUG_BREAK(); 225 | return false; 226 | } 227 | 228 | return true; 229 | } 230 | 231 | bool TestPacketCount_Header() 232 | { 233 | uint8_t buffer[siamese::kMaxPacketCountFieldBytes]; 234 | 235 | static const unsigned kCountsCount = 9; 236 | static const unsigned Counts[kCountsCount] = { 237 | 1, 2, 3, 126, 127, 128, 129, SIAMESE_MAX_PACKETS - 1, SIAMESE_MAX_PACKETS 238 | }; 239 | for (unsigned i = 0; i < kCountsCount; ++i) 240 | { 241 | unsigned count = Counts[i]; 242 | 243 | unsigned written = siamese::SerializeHeader_PacketCount(count, buffer); 244 | if (written < 1 || written > siamese::kMaxPacketCountFieldBytes) 245 | { 246 | SIAMESE_DEBUG_BREAK(); 247 | return false; 248 | } 249 | unsigned countOut = 0xfffffff; 250 | int bytes = siamese::DeserializeHeader_PacketCount(buffer, (unsigned)sizeof(buffer), countOut); 251 | if (bytes < 0 || bytes != (int)written) 252 | { 253 | SIAMESE_DEBUG_BREAK(); 254 | return false; 255 | } 256 | if (count != countOut) 257 | { 258 | SIAMESE_DEBUG_BREAK(); 259 | return false; 260 | } 261 | } 262 | 263 | return true; 264 | } 265 | 266 | bool TestPacketCount_Footer() 267 | { 268 | uint8_t buffer[siamese::kMaxPacketCountFieldBytes]; 269 | 270 | static const unsigned kCountsCount = 9; 271 | static const unsigned Counts[kCountsCount] = { 272 | 1, 2, 3, 126, 127, 128, 129, SIAMESE_MAX_PACKETS - 1, SIAMESE_MAX_PACKETS 273 | }; 274 | for (unsigned i = 0; i < kCountsCount; ++i) 275 | { 276 | unsigned count = Counts[i]; 277 | 278 | unsigned written = siamese::SerializeFooter_PacketCount(count, buffer); 279 | if (written < 1 || written > siamese::kMaxPacketCountFieldBytes) 280 | { 281 | SIAMESE_DEBUG_BREAK(); 282 | return false; 283 | } 284 | unsigned countOut = 0xfffffff; 285 | int bytes = siamese::DeserializeFooter_PacketCount(buffer, written, countOut); 286 | if (bytes < 0 || bytes != (int)written) 287 | { 288 | SIAMESE_DEBUG_BREAK(); 289 | return false; 290 | } 291 | if (count != countOut) 292 | { 293 | SIAMESE_DEBUG_BREAK(); 294 | return false; 295 | } 296 | } 297 | 298 | return true; 299 | } 300 | 301 | bool TestPacketLength_Header() 302 | { 303 | uint8_t buffer[siamese::kMaxPacketLengthFieldBytes]; 304 | 305 | static const unsigned kCountsCount = 16; 306 | static const unsigned Counts[kCountsCount] = { 307 | 1, 2, 3, 0x7d, 0x7e, 0x7f, 0x80, 0x81, 308 | 0x3fff - 1, 0x3fff, 0x3fff + 1, 0x1fffff - 1, 309 | 0x1fffff, 0x1fffff + 1, SIAMESE_MAX_PACKET_BYTES - 1, 310 | SIAMESE_MAX_PACKET_BYTES 311 | }; 312 | for (unsigned i = 0; i < kCountsCount; ++i) 313 | { 314 | unsigned count = Counts[i]; 315 | 316 | unsigned written = siamese::SerializeHeader_PacketLength(count, buffer); 317 | if (written < 1 || written > siamese::kMaxPacketLengthFieldBytes) 318 | { 319 | SIAMESE_DEBUG_BREAK(); 320 | return false; 321 | } 322 | unsigned countOut = 0xfffffff; 323 | int bytes = siamese::DeserializeHeader_PacketLength(buffer, (unsigned)sizeof(buffer), countOut); 324 | if (bytes < 0 || bytes != (int)written) 325 | { 326 | SIAMESE_DEBUG_BREAK(); 327 | return false; 328 | } 329 | if (count != countOut) 330 | { 331 | SIAMESE_DEBUG_BREAK(); 332 | return false; 333 | } 334 | } 335 | 336 | return true; 337 | } 338 | 339 | bool TestPacketLength_Footer() 340 | { 341 | uint8_t buffer[siamese::kMaxPacketLengthFieldBytes]; 342 | 343 | static const unsigned kCountsCount = 16; 344 | static const unsigned Counts[kCountsCount] = { 345 | 1, 2, 3, 0x7d, 0x7e, 0x7f, 0x80, 0x81, 346 | 0x3fff - 1, 0x3fff, 0x3fff + 1, 0x1fffff - 1, 347 | 0x1fffff, 0x1fffff + 1, SIAMESE_MAX_PACKET_BYTES - 1, 348 | SIAMESE_MAX_PACKET_BYTES 349 | }; 350 | for (unsigned i = 0; i < kCountsCount; ++i) 351 | { 352 | unsigned count = Counts[i]; 353 | 354 | unsigned written = siamese::SerializeFooter_PacketLength(count, buffer); 355 | if (written < 1 || written > siamese::kMaxPacketLengthFieldBytes) 356 | { 357 | SIAMESE_DEBUG_BREAK(); 358 | return false; 359 | } 360 | unsigned countOut = 0xfffffff; 361 | int bytes = siamese::DeserializeFooter_PacketLength(buffer, written, countOut); 362 | if (bytes < 0 || bytes != (int)written) 363 | { 364 | SIAMESE_DEBUG_BREAK(); 365 | return false; 366 | } 367 | if (count != countOut) 368 | { 369 | SIAMESE_DEBUG_BREAK(); 370 | return false; 371 | } 372 | } 373 | 374 | return true; 375 | } 376 | 377 | bool TestPacketNum_Header() 378 | { 379 | uint8_t buffer[siamese::kMaxPacketNumEncodedBytes]; 380 | 381 | static const unsigned kColumnStartsCount = 17; 382 | static const unsigned ColumnStarts[kColumnStartsCount] = { 383 | 0, 1, 2, 3, 4, 0x7e, 0x7f, 0x80, 0x81, 0x3ffe, 0x3fff, 0x4000, 384 | 0x4001, 0x4002, 0x4003, SIAMESE_PACKET_NUM_MAX - 1, SIAMESE_PACKET_NUM_MAX 385 | }; 386 | for (unsigned i = 0; i < kColumnStartsCount; ++i) 387 | { 388 | unsigned count = ColumnStarts[i]; 389 | 390 | unsigned written = siamese::SerializeHeader_PacketNum(count, buffer); 391 | if (written < 1 || written > siamese::kMaxPacketNumEncodedBytes) 392 | { 393 | SIAMESE_DEBUG_BREAK(); 394 | return false; 395 | } 396 | unsigned countOut = 0xfffffff; 397 | int bytes = siamese::DeserializeHeader_PacketNum(buffer, (unsigned)sizeof(buffer), countOut); 398 | if (bytes < 0 || bytes != (int)written) 399 | { 400 | SIAMESE_DEBUG_BREAK(); 401 | return false; 402 | } 403 | if (count != countOut) 404 | { 405 | SIAMESE_DEBUG_BREAK(); 406 | return false; 407 | } 408 | } 409 | 410 | return true; 411 | } 412 | 413 | bool TestPacketNum_Footer() 414 | { 415 | uint8_t buffer[siamese::kMaxPacketNumEncodedBytes]; 416 | 417 | static const unsigned kColumnStartsCount = 17; 418 | static const unsigned ColumnStarts[kColumnStartsCount] = { 419 | 0, 1, 2, 3, 4, 0x7e, 0x7f, 0x80, 0x81, 0x3ffe, 0x3fff, 0x4000, 420 | 0x4001, 0x4002, 0x4003, SIAMESE_PACKET_NUM_MAX - 1, SIAMESE_PACKET_NUM_MAX 421 | }; 422 | for (unsigned i = 0; i < kColumnStartsCount; ++i) 423 | { 424 | unsigned count = ColumnStarts[i]; 425 | 426 | unsigned written = siamese::SerializeFooter_PacketNum(count, buffer); 427 | if (written < 1 || written > siamese::kMaxPacketNumEncodedBytes) 428 | { 429 | SIAMESE_DEBUG_BREAK(); 430 | return false; 431 | } 432 | unsigned countOut = 0xfffffff; 433 | int bytes = siamese::DeserializeFooter_PacketNum(buffer, written, countOut); 434 | if (bytes < 0 || bytes != (int)written) 435 | { 436 | SIAMESE_DEBUG_BREAK(); 437 | return false; 438 | } 439 | if (count != countOut) 440 | { 441 | SIAMESE_DEBUG_BREAK(); 442 | return false; 443 | } 444 | } 445 | 446 | return true; 447 | } 448 | 449 | bool TestRecoveryMetadata_Footer() 450 | { 451 | uint8_t buffer[siamese::kMaxRecoveryMetadataBytes]; 452 | 453 | static const unsigned kCountsCount = 9; 454 | static const unsigned Counts[kCountsCount] = { 455 | 1, 2, 3, 126, 127, 128, 129, SIAMESE_MAX_PACKETS - 1, SIAMESE_MAX_PACKETS 456 | }; 457 | 458 | static const unsigned kRowsCount = siamese::kRowPeriod; 459 | static unsigned Rows[kRowsCount] = { 460 | }; 461 | for (unsigned i = 0; i < kRowsCount; ++i) 462 | Rows[i] = i; 463 | 464 | static const unsigned kColumnStartsCount = 17; 465 | static const unsigned ColumnStarts[kColumnStartsCount] = { 466 | 0, 1, 2, 3, 4, 0x7e, 0x7f, 0x80, 0x81, 0x3ffe, 0x3fff, 0x4000, 467 | 0x4001, 0x4002, 0x4003, SIAMESE_PACKET_NUM_MAX - 1, SIAMESE_PACKET_NUM_MAX 468 | }; 469 | 470 | for (unsigned i = 0; i < kCountsCount; ++i) 471 | for (unsigned j = 0; j < kRowsCount; ++j) 472 | for (unsigned k = 0; k < kCountsCount; ++k) 473 | for (unsigned m = 0; m < kColumnStartsCount; ++m) 474 | { 475 | siamese::RecoveryMetadata metadata; 476 | metadata.SumCount = Counts[i]; 477 | metadata.Row = Rows[j]; 478 | metadata.LDPCCount = Counts[k]; 479 | metadata.ColumnStart = ColumnStarts[m]; 480 | 481 | if (metadata.LDPCCount > metadata.SumCount) 482 | continue; 483 | 484 | unsigned written = siamese::SerializeFooter_RecoveryMetadata(metadata, buffer); 485 | if (written < 1 || written > siamese::kMaxRecoveryMetadataBytes) 486 | { 487 | SIAMESE_DEBUG_BREAK(); 488 | return false; 489 | } 490 | siamese::RecoveryMetadata metadataOut; 491 | int bytes = siamese::DeserializeFooter_RecoveryMetadata(buffer, written, metadataOut); 492 | if (bytes < 0 || bytes != (int)written) 493 | { 494 | SIAMESE_DEBUG_BREAK(); 495 | return false; 496 | } 497 | if (metadata.SumCount > 1) 498 | { 499 | if (metadata.ColumnStart != metadataOut.ColumnStart || 500 | metadata.LDPCCount != metadataOut.LDPCCount || 501 | metadata.Row != metadataOut.Row || 502 | metadata.SumCount != metadataOut.SumCount) 503 | { 504 | SIAMESE_DEBUG_BREAK(); 505 | return false; 506 | } 507 | } 508 | else 509 | { 510 | if (metadata.ColumnStart != metadataOut.ColumnStart || 511 | metadata.SumCount != metadataOut.SumCount) 512 | { 513 | SIAMESE_DEBUG_BREAK(); 514 | return false; 515 | } 516 | if (metadataOut.LDPCCount != 1 || 517 | metadataOut.Row != 0) 518 | { 519 | SIAMESE_DEBUG_BREAK(); 520 | return false; 521 | } 522 | } 523 | } 524 | 525 | return true; 526 | } 527 | 528 | bool TestNACKLossRange_Header() 529 | { 530 | uint8_t buffer[siamese::kMaxLossRangeFieldBytes]; 531 | 532 | static const unsigned kColumnStartsCount = 15; 533 | static const unsigned ColumnStarts[kColumnStartsCount] = { 534 | 0, 1, 2, 3, (1 << 5) - 1, (1 << 5), (1 << 5) + 1, 535 | (1 << (5 + 7)) - 1, (1 << (5 + 7)), (1 << (5 + 7)) + 1, 536 | (1 << (5 + 7 + 7)) - 1, (1 << (5 + 7 + 7)), (1 << (5 + 7 + 7)) + 1, 537 | SIAMESE_PACKET_NUM_MAX - 1, SIAMESE_PACKET_NUM_MAX 538 | }; 539 | 540 | static const unsigned kLossCountM1Count = 14; 541 | static const unsigned LossCountM1s[kLossCountM1Count] = { 542 | 0, 1, 2, 3, 4, 5, (1 << 7) - 1, (1 << 7), (1 << 7) - 1, 543 | (1 << (7 + 7)) - 1, (1 << (7 + 7)), (1 << (7 + 7)) + 1, 544 | SIAMESE_PACKET_NUM_MAX - 1, SIAMESE_PACKET_NUM_MAX 545 | }; 546 | 547 | for (unsigned i = 0; i < kColumnStartsCount; ++i) 548 | for (unsigned j = 0; j < kLossCountM1Count; ++j) 549 | { 550 | unsigned columnStart = ColumnStarts[i]; 551 | unsigned lossCountM1 = LossCountM1s[j]; 552 | 553 | unsigned written = siamese::SerializeHeader_NACKLossRange(columnStart, lossCountM1, buffer); 554 | if (written < 1 || written > siamese::kMaxRecoveryMetadataBytes) 555 | { 556 | SIAMESE_DEBUG_BREAK(); 557 | return false; 558 | } 559 | unsigned columnStartOut; 560 | unsigned lossCountM1Out; 561 | int bytes = siamese::DeserializeHeader_NACKLossRange(buffer, (unsigned)sizeof(buffer), columnStartOut, lossCountM1Out); 562 | if (bytes < 0 || bytes != (int)written) 563 | { 564 | SIAMESE_DEBUG_BREAK(); 565 | return false; 566 | } 567 | if (columnStart != columnStartOut || 568 | lossCountM1 != lossCountM1Out) 569 | { 570 | SIAMESE_DEBUG_BREAK(); 571 | return false; 572 | } 573 | } 574 | 575 | return true; 576 | } 577 | 578 | bool TestSerializers() 579 | { 580 | if (!TestPODSerialization16()) 581 | return false; 582 | if (!TestPODSerialization24()) 583 | return false; 584 | if (!TestPODSerialization32()) 585 | return false; 586 | if (!TestPODSerialization64()) 587 | return false; 588 | 589 | if (!TestByteStream()) 590 | return false; 591 | 592 | if (!TestPacketCount_Header()) 593 | return false; 594 | if (!TestPacketCount_Footer()) 595 | return false; 596 | 597 | if (!TestPacketLength_Header()) 598 | return false; 599 | if (!TestPacketLength_Footer()) 600 | return false; 601 | 602 | if (!TestPacketNum_Header()) 603 | return false; 604 | if (!TestPacketNum_Footer()) 605 | return false; 606 | 607 | if (!TestRecoveryMetadata_Footer()) 608 | return false; 609 | 610 | if (!TestNACKLossRange_Header()) 611 | return false; 612 | 613 | return true; 614 | } 615 | 616 | 617 | #ifdef ENABLE_TEST_SERIALIZERS 618 | 619 | int main() 620 | { 621 | if (!TestSerializers()) 622 | { 623 | cout << "FAIL" << endl; 624 | return -1; 625 | } 626 | 627 | cout << "Success!" << endl; 628 | return 0; 629 | } 630 | 631 | #endif // ENABLE_TEST_SERIALIZERS 632 | --------------------------------------------------------------------------------