├── .clang-format ├── .clang-tidy ├── .gitattributes ├── .github ├── FUNDING.yml └── workflows │ ├── build-and-test.yml │ └── release.yml ├── .gitignore ├── .gitmodules ├── .readthedocs.yaml ├── .vscode ├── c_cpp_properties.json ├── extensions.json ├── launch.json ├── settings.json └── tasks.json ├── AUTHORS ├── CHANGELOG.md ├── CMakeLists.txt ├── CMakePresets.json ├── LICENSE ├── README.md ├── cmake ├── i686-w64-mingw32-gcc.cmake └── x86_64-w64-mingw32-gcc.cmake ├── dev └── main.c ├── docs ├── Makefile ├── api-reference │ ├── index.rst │ └── lwrb.rst ├── authors │ └── index.rst ├── changelog │ └── index.rst ├── conf.py ├── doxyfile.doxy ├── examples_src │ ├── example_advance_1.c │ ├── example_advance_2.c │ ├── example_dma_skip.c │ ├── example_events.c │ ├── example_index.c │ ├── example_minimal.c │ ├── example_skip_1.c │ ├── example_skip_2.c │ ├── example_thread_safety.c │ ├── example_tt_buff_size.c │ └── example_tt_buff_size_log.c ├── get-started │ └── index.rst ├── index.rst ├── make.bat ├── requirements.txt ├── static │ ├── css │ │ ├── common.css │ │ └── custom.css │ ├── dark-light │ │ ├── checked.svg │ │ ├── common-dark-light.css │ │ ├── dark-mode-toggle.mjs │ │ ├── dark.css │ │ ├── light.css │ │ ├── moon.png │ │ ├── moon.svg │ │ ├── sun.png │ │ ├── sun.svg │ │ └── unchecked.svg │ └── images │ │ ├── buff_cases.svg │ │ ├── buff_cases.xml │ │ ├── buff_lin_read_skip.svg │ │ ├── buff_lin_read_skip.xml │ │ ├── buff_lin_write_advance.svg │ │ ├── buff_lin_write_advance.xml │ │ ├── buff_thread_safety_2_main_irq_write.svg │ │ ├── buff_thread_safety_2_main_irq_write.xml │ │ ├── buff_thread_safety_2_thread_read.svg │ │ ├── buff_thread_safety_2_thread_read.xml │ │ ├── buff_thread_safety_2_thread_read_write.svg │ │ ├── buff_thread_safety_2_thread_read_write.xml │ │ ├── buff_thread_safety_2_thread_write.svg │ │ ├── buff_thread_safety_2_thread_write.xml │ │ ├── buff_thread_safety_single_read_write.svg │ │ ├── buff_thread_safety_single_read_write.xml │ │ ├── logo.drawio │ │ ├── logo.svg │ │ ├── logo_tm.png │ │ └── logo_tm_full.png ├── tips-tricks │ └── index.rst └── user-manual │ ├── events.rst │ ├── how-it-works.rst │ ├── hw-dma-usage.rst │ ├── index.rst │ └── thread-safety.rst ├── library.json └── lwrb ├── CMakeLists.txt ├── library.cmake ├── src ├── include │ └── lwrb │ │ └── lwrb.h └── lwrb │ ├── lwrb.c │ └── lwrb_ex.c └── test ├── .gitignore ├── CMakeLists.txt ├── README.md ├── llvm-cov.sh ├── test.c └── test_ex.c /.clang-format: -------------------------------------------------------------------------------- 1 | # Language part removed. With clang-format >=20.1, the C and Cpp are separately handled, 2 | # so either there is no language at all, or we need to create 2 formats for C and Cpp, separately 3 | 4 | --- 5 | # Language: Cpp 6 | # BasedOnStyle: LLVM 7 | AccessModifierOffset: -2 8 | AlignAfterOpenBracket: Align 9 | AlignArrayOfStructures: None 10 | AlignConsecutiveMacros: 11 | Enabled: true 12 | AcrossEmptyLines: true 13 | AcrossComments: true 14 | AlignConsecutiveAssignments: None 15 | AlignConsecutiveBitFields: 16 | Enabled: true 17 | AcrossEmptyLines: true 18 | AcrossComments: true 19 | AlignConsecutiveDeclarations: None 20 | AlignEscapedNewlines: Right 21 | AlignOperands: Align 22 | SortIncludes: true 23 | InsertBraces: true # Control statements must have curly brackets 24 | AlignTrailingComments: true 25 | AllowAllArgumentsOnNextLine: true 26 | AllowAllParametersOfDeclarationOnNextLine: true 27 | AllowShortEnumsOnASingleLine: true 28 | AllowShortBlocksOnASingleLine: Empty 29 | AllowShortCaseLabelsOnASingleLine: true 30 | AllowShortFunctionsOnASingleLine: All 31 | AllowShortLambdasOnASingleLine: All 32 | AllowShortIfStatementsOnASingleLine: Never 33 | AllowShortLoopsOnASingleLine: false 34 | AlwaysBreakAfterDefinitionReturnType: None 35 | AlwaysBreakAfterReturnType: AllDefinitions 36 | AlwaysBreakBeforeMultilineStrings: false 37 | AlwaysBreakTemplateDeclarations: Yes 38 | AttributeMacros: 39 | - __capability 40 | BinPackArguments: true 41 | BinPackParameters: true 42 | BraceWrapping: 43 | AfterCaseLabel: false 44 | AfterClass: false 45 | AfterControlStatement: Never 46 | AfterEnum: false 47 | AfterFunction: false 48 | AfterNamespace: false 49 | AfterObjCDeclaration: false 50 | AfterStruct: false 51 | AfterUnion: false 52 | AfterExternBlock: false 53 | BeforeCatch: false 54 | BeforeElse: false 55 | BeforeLambdaBody: false 56 | BeforeWhile: false 57 | IndentBraces: false 58 | SplitEmptyFunction: true 59 | SplitEmptyRecord: true 60 | SplitEmptyNamespace: true 61 | BreakBeforeBinaryOperators: NonAssignment 62 | BreakBeforeConceptDeclarations: true 63 | BreakBeforeBraces: Attach 64 | BreakBeforeInheritanceComma: false 65 | BreakInheritanceList: BeforeColon 66 | BreakBeforeTernaryOperators: true 67 | BreakConstructorInitializersBeforeComma: false 68 | BreakConstructorInitializers: BeforeColon 69 | BreakAfterJavaFieldAnnotations: false 70 | BreakStringLiterals: true 71 | ColumnLimit: 120 72 | CommentPragmas: "^ IWYU pragma:" 73 | QualifierAlignment: Leave 74 | CompactNamespaces: false 75 | ConstructorInitializerIndentWidth: 4 76 | ContinuationIndentWidth: 4 77 | Cpp11BracedListStyle: true 78 | DeriveLineEnding: true 79 | DerivePointerAlignment: false 80 | DisableFormat: false 81 | EmptyLineAfterAccessModifier: Never 82 | EmptyLineBeforeAccessModifier: LogicalBlock 83 | ExperimentalAutoDetectBinPacking: false 84 | PackConstructorInitializers: BinPack 85 | BasedOnStyle: "" 86 | ConstructorInitializerAllOnOneLineOrOnePerLine: false 87 | AllowAllConstructorInitializersOnNextLine: true 88 | FixNamespaceComments: true 89 | ForEachMacros: 90 | - foreach 91 | - Q_FOREACH 92 | - BOOST_FOREACH 93 | IfMacros: 94 | - KJ_IF_MAYBE 95 | IncludeBlocks: Preserve 96 | IncludeCategories: 97 | - Regex: "^<(.*)>" 98 | Priority: 0 99 | - Regex: '^"(.*)"' 100 | Priority: 1 101 | - Regex: "(.*)" 102 | Priority: 2 103 | IncludeIsMainRegex: "(Test)?$" 104 | IncludeIsMainSourceRegex: "" 105 | IndentAccessModifiers: false 106 | IndentCaseLabels: true 107 | IndentCaseBlocks: false 108 | IndentGotoLabels: true 109 | IndentPPDirectives: None 110 | IndentExternBlock: AfterExternBlock 111 | IndentRequires: true 112 | IndentWidth: 4 113 | IndentWrappedFunctionNames: false 114 | InsertTrailingCommas: None 115 | JavaScriptQuotes: Leave 116 | JavaScriptWrapImports: true 117 | KeepEmptyLinesAtTheStartOfBlocks: true 118 | LambdaBodyIndentation: Signature 119 | MacroBlockBegin: "" 120 | MacroBlockEnd: "" 121 | MaxEmptyLinesToKeep: 1 122 | NamespaceIndentation: None 123 | ObjCBinPackProtocolList: Auto 124 | ObjCBlockIndentWidth: 2 125 | ObjCBreakBeforeNestedBlockParam: true 126 | ObjCSpaceAfterProperty: false 127 | ObjCSpaceBeforeProtocolList: true 128 | PenaltyBreakAssignment: 2 129 | PenaltyBreakBeforeFirstCallParameter: 19 130 | PenaltyBreakComment: 300 131 | PenaltyBreakFirstLessLess: 120 132 | PenaltyBreakOpenParenthesis: 0 133 | PenaltyBreakString: 1000 134 | PenaltyBreakTemplateDeclaration: 10 135 | PenaltyExcessCharacter: 1000000 136 | PenaltyReturnTypeOnItsOwnLine: 60 137 | PenaltyIndentedWhitespace: 0 138 | PointerAlignment: Left 139 | PPIndentWidth: -1 140 | ReferenceAlignment: Pointer 141 | ReflowComments: false 142 | RemoveBracesLLVM: false 143 | SeparateDefinitionBlocks: Always 144 | ShortNamespaceLines: 1 145 | SortJavaStaticImport: Before 146 | SortUsingDeclarations: true 147 | SpaceAfterCStyleCast: false 148 | SpaceAfterLogicalNot: false 149 | SpaceAfterTemplateKeyword: true 150 | SpaceBeforeAssignmentOperators: true 151 | SpaceBeforeCaseColon: false 152 | SpaceBeforeParens: ControlStatements 153 | SpaceBeforeParensOptions: 154 | AfterControlStatements: true 155 | AfterForeachMacros: true 156 | AfterFunctionDefinitionName: false 157 | AfterFunctionDeclarationName: false 158 | AfterIfMacros: true 159 | AfterOverloadedOperator: false 160 | BeforeNonEmptyParentheses: false 161 | SpaceAroundPointerQualifiers: Default 162 | SpaceBeforeRangeBasedForLoopColon: true 163 | SpaceInEmptyBlock: false 164 | SpaceInEmptyParentheses: false 165 | SpacesBeforeTrailingComments: 1 166 | SpacesInAngles: Never 167 | SpacesInConditionalStatement: false 168 | SpacesInContainerLiterals: true 169 | SpacesInCStyleCastParentheses: false 170 | SpacesInLineCommentPrefix: 171 | Minimum: 1 172 | Maximum: -1 173 | SpacesInParentheses: false 174 | SpacesInSquareBrackets: false 175 | SpaceBeforeSquareBrackets: false 176 | BitFieldColonSpacing: Both 177 | Standard: Latest 178 | StatementAttributeLikeMacros: 179 | - Q_EMIT 180 | StatementMacros: 181 | - Q_UNUSED 182 | - QT_REQUIRE_VERSION 183 | TabWidth: 8 184 | UseCRLF: false 185 | UseTab: Never 186 | WhitespaceSensitiveMacros: 187 | - STRINGIZE 188 | - PP_STRINGIZE 189 | - BOOST_PP_STRINGIZE 190 | - NS_SWIFT_NAME 191 | - CF_SWIFT_NAME 192 | SpaceBeforeCpp11BracedList: false 193 | SpaceBeforeCtorInitializerColon: true 194 | SpaceBeforeInheritanceColon: true 195 | --- 196 | 197 | -------------------------------------------------------------------------------- /.clang-tidy: -------------------------------------------------------------------------------- 1 | --- 2 | Checks: "*, 3 | -abseil-*, 4 | -altera-*, 5 | -android-*, 6 | -fuchsia-*, 7 | -google-*, 8 | -llvm*, 9 | -modernize-use-trailing-return-type, 10 | -zircon-*, 11 | -readability-else-after-return, 12 | -readability-static-accessed-through-instance, 13 | -readability-avoid-const-params-in-decls, 14 | -cppcoreguidelines-non-private-member-variables-in-classes, 15 | -misc-non-private-member-variables-in-classes, 16 | " 17 | WarningsAsErrors: '' 18 | HeaderFilterRegex: '' 19 | FormatStyle: none -------------------------------------------------------------------------------- /.gitattributes: -------------------------------------------------------------------------------- 1 | # Auto detect text files and perform LF normalization 2 | * text=auto 3 | 4 | # Custom for Visual Studio 5 | *.cs diff=csharp 6 | 7 | # Standard to msysgit 8 | *.doc diff=astextplain 9 | *.DOC diff=astextplain 10 | *.docx diff=astextplain 11 | *.DOCX diff=astextplain 12 | *.dot diff=astextplain 13 | *.DOT diff=astextplain 14 | *.pdf diff=astextplain 15 | *.PDF diff=astextplain 16 | *.rtf diff=astextplain 17 | *.RTF diff=astextplain -------------------------------------------------------------------------------- /.github/FUNDING.yml: -------------------------------------------------------------------------------- 1 | # These are supported funding model platforms 2 | 3 | custom: ['paypal.me/tilz0R'] 4 | -------------------------------------------------------------------------------- /.github/workflows/build-and-test.yml: -------------------------------------------------------------------------------- 1 | name: Windows CMake Build & Test 2 | 3 | on: 4 | push: 5 | branches: 6 | - develop 7 | pull_request: 8 | branches: 9 | - develop 10 | 11 | jobs: 12 | build: 13 | runs-on: windows-latest 14 | 15 | steps: 16 | - name: Checkout Repository 17 | uses: actions/checkout@v4 18 | 19 | - name: Install MinGW 20 | run: | 21 | choco install mingw --version=12.2.0 -y 22 | echo "C:\ProgramData\chocolatey\lib\mingw\tools\install\mingw64\bin" >> $GITHUB_PATH 23 | gcc --version 24 | 25 | - name: Build 26 | run: | 27 | mkdir __build__ 28 | cd __build__ 29 | cmake -DCMAKE_C_COMPILER=gcc -DCMAKE_CXX_COMPILER=gcc -S.. -G Ninja 30 | cmake --build . 31 | 32 | - name: Run Tests 33 | working-directory: __build__ 34 | run: | 35 | ctest . --output-on-failure 36 | -------------------------------------------------------------------------------- /.github/workflows/release.yml: -------------------------------------------------------------------------------- 1 | on: 2 | push: 3 | # Sequence of patterns matched against refs/tags 4 | tags: 5 | - 'v*' # Push events to matching v*, i.e. v1.0, v20.15.10 6 | 7 | name: Create Release 8 | 9 | jobs: 10 | build: 11 | name: Create Release 12 | runs-on: ubuntu-latest 13 | steps: 14 | - name: Checkout code 15 | uses: actions/checkout@v2 16 | - name: Create Release 17 | id: create_release 18 | uses: actions/create-release@v1 19 | env: 20 | GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} # This token is provided by Actions, you do not need to create your own token 21 | with: 22 | tag_name: ${{ github.ref }} 23 | release_name: Release ${{ github.ref }} 24 | body: | 25 | See the [CHANGELOG](CHANGELOG.md) 26 | draft: false 27 | prerelease: false -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | #Build Keil files 2 | *.rar 3 | *.o 4 | *.d 5 | *.crf 6 | *.htm 7 | *.dep 8 | *.map 9 | *.bak 10 | *.axf 11 | *.lnp 12 | *.lst 13 | *.ini 14 | *.scvd 15 | *.iex 16 | *.sct 17 | *.MajerleT 18 | *.tjuln 19 | *.tilen 20 | *.dbgconf 21 | *.uvguix 22 | *.uvoptx 23 | *.__i 24 | *.i 25 | *.txt 26 | !docs/*.txt 27 | !CMakeLists.txt 28 | RTE/ 29 | 30 | *debug 31 | 32 | # IAR Settings 33 | **/settings/*.crun 34 | **/settings/*.dbgdt 35 | **/settings/*.cspy 36 | **/settings/*.cspy.* 37 | **/settings/*.xcl 38 | **/settings/*.dni 39 | **/settings/*.wsdt 40 | **/settings/*.wspos 41 | 42 | # IAR Debug Exe 43 | **/Exe/*.sim 44 | 45 | # IAR Debug Obj 46 | **/Obj/*.pbd 47 | **/Obj/*.pbd.* 48 | **/Obj/*.pbi 49 | **/Obj/*.pbi.* 50 | 51 | *.TMP 52 | /docs_src/x_Doxyfile.doxy 53 | 54 | .DS_Store 55 | 56 | ## Ignore Visual Studio temporary files, build results, and 57 | ## files generated by popular Visual Studio add-ons. 58 | ## 59 | ## Get latest from https://github.com/github/gitignore/blob/master/VisualStudio.gitignore 60 | 61 | # User-specific files 62 | *.suo 63 | *.user 64 | *.userosscache 65 | *.sln.docstates 66 | 67 | # User-specific files (MonoDevelop/Xamarin Studio) 68 | *.userprefs 69 | 70 | # Build results 71 | [Dd]ebug/ 72 | [Dd]ebugPublic/ 73 | [Rr]elease/ 74 | [Rr]eleases/ 75 | [Dd]ebug*/ 76 | x64/ 77 | x86/ 78 | bld/ 79 | [Bb]in/ 80 | [Oo]bj/ 81 | [Ll]og/ 82 | _build/ 83 | build/ 84 | __build__/ 85 | 86 | # Visual Studio 2015/2017 cache/options directory 87 | .vs/ 88 | # Uncomment if you have tasks that create the project's static files in wwwroot 89 | #wwwroot/ 90 | 91 | # Visual Studio 2017 auto generated files 92 | Generated\ Files/ 93 | 94 | # MSTest test Results 95 | [Tt]est[Rr]esult*/ 96 | [Bb]uild[Ll]og.* 97 | 98 | # NUNIT 99 | *.VisualState.xml 100 | TestResult.xml 101 | 102 | # Build Results of an ATL Project 103 | [Dd]ebugPS/ 104 | [Rr]eleasePS/ 105 | dlldata.c 106 | 107 | # Benchmark Results 108 | BenchmarkDotNet.Artifacts/ 109 | 110 | # .NET Core 111 | project.lock.json 112 | project.fragment.lock.json 113 | artifacts/ 114 | **/Properties/launchSettings.json 115 | 116 | # StyleCop 117 | StyleCopReport.xml 118 | 119 | # Files built by Visual Studio 120 | *_i.c 121 | *_p.c 122 | *_i.h 123 | *.ilk 124 | *.meta 125 | *.obj 126 | *.pch 127 | *.pdb 128 | *.pgc 129 | *.pgd 130 | *.rsp 131 | *.sbr 132 | *.tlb 133 | *.tli 134 | *.tlh 135 | *.tmp 136 | *.tmp_proj 137 | *.log 138 | *.vspscc 139 | *.vssscc 140 | .builds 141 | *.pidb 142 | *.svclog 143 | *.scc 144 | *.out 145 | *.sim 146 | 147 | # Chutzpah Test files 148 | _Chutzpah* 149 | 150 | # Visual C++ cache files 151 | ipch/ 152 | *.aps 153 | *.ncb 154 | *.opendb 155 | *.opensdf 156 | *.sdf 157 | *.cachefile 158 | *.VC.db 159 | *.VC.VC.opendb 160 | 161 | # Visual Studio profiler 162 | *.psess 163 | *.vsp 164 | *.vspx 165 | *.sap 166 | 167 | # Visual Studio Trace Files 168 | *.e2e 169 | 170 | # TFS 2012 Local Workspace 171 | $tf/ 172 | 173 | # Guidance Automation Toolkit 174 | *.gpState 175 | 176 | # ReSharper is a .NET coding add-in 177 | _ReSharper*/ 178 | *.[Rr]e[Ss]harper 179 | *.DotSettings.user 180 | 181 | # JustCode is a .NET coding add-in 182 | .JustCode 183 | 184 | # TeamCity is a build add-in 185 | _TeamCity* 186 | 187 | # DotCover is a Code Coverage Tool 188 | *.dotCover 189 | 190 | # AxoCover is a Code Coverage Tool 191 | .axoCover/* 192 | !.axoCover/settings.json 193 | 194 | # Visual Studio code coverage results 195 | *.coverage 196 | *.coveragexml 197 | 198 | # NCrunch 199 | _NCrunch_* 200 | .*crunch*.local.xml 201 | nCrunchTemp_* 202 | 203 | # MightyMoose 204 | *.mm.* 205 | AutoTest.Net/ 206 | 207 | # Web workbench (sass) 208 | .sass-cache/ 209 | 210 | # Installshield output folder 211 | [Ee]xpress/ 212 | 213 | # DocProject is a documentation generator add-in 214 | DocProject/buildhelp/ 215 | DocProject/Help/*.HxT 216 | DocProject/Help/*.HxC 217 | DocProject/Help/*.hhc 218 | DocProject/Help/*.hhk 219 | DocProject/Help/*.hhp 220 | DocProject/Help/Html2 221 | DocProject/Help/html 222 | 223 | # Click-Once directory 224 | publish/ 225 | 226 | # Publish Web Output 227 | *.[Pp]ublish.xml 228 | *.azurePubxml 229 | # Note: Comment the next line if you want to checkin your web deploy settings, 230 | # but database connection strings (with potential passwords) will be unencrypted 231 | *.pubxml 232 | *.publishproj 233 | 234 | # Microsoft Azure Web App publish settings. Comment the next line if you want to 235 | # checkin your Azure Web App publish settings, but sensitive information contained 236 | # in these scripts will be unencrypted 237 | PublishScripts/ 238 | 239 | # NuGet Packages 240 | *.nupkg 241 | # The packages folder can be ignored because of Package Restore 242 | **/[Pp]ackages/* 243 | # except build/, which is used as an MSBuild target. 244 | !**/[Pp]ackages/build/ 245 | # Uncomment if necessary however generally it will be regenerated when needed 246 | #!**/[Pp]ackages/repositories.config 247 | # NuGet v3's project.json files produces more ignorable files 248 | *.nuget.props 249 | *.nuget.targets 250 | 251 | # Microsoft Azure Build Output 252 | csx/ 253 | *.build.csdef 254 | 255 | # Microsoft Azure Emulator 256 | ecf/ 257 | rcf/ 258 | 259 | # Windows Store app package directories and files 260 | AppPackages/ 261 | BundleArtifacts/ 262 | Package.StoreAssociation.xml 263 | _pkginfo.txt 264 | *.appx 265 | 266 | # Visual Studio cache files 267 | # files ending in .cache can be ignored 268 | *.[Cc]ache 269 | # but keep track of directories ending in .cache 270 | !*.[Cc]ache/ 271 | 272 | # Others 273 | ClientBin/ 274 | ~$* 275 | *~ 276 | *.dbmdl 277 | *.dbproj.schemaview 278 | *.jfm 279 | *.pfx 280 | *.publishsettings 281 | orleans.codegen.cs 282 | 283 | # Including strong name files can present a security risk 284 | # (https://github.com/github/gitignore/pull/2483#issue-259490424) 285 | #*.snk 286 | 287 | # Since there are multiple workflows, uncomment next line to ignore bower_components 288 | # (https://github.com/github/gitignore/pull/1529#issuecomment-104372622) 289 | #bower_components/ 290 | 291 | # RIA/Silverlight projects 292 | Generated_Code/ 293 | 294 | # Backup & report files from converting an old project file 295 | # to a newer Visual Studio version. Backup files are not needed, 296 | # because we have git ;-) 297 | _UpgradeReport_Files/ 298 | Backup*/ 299 | UpgradeLog*.XML 300 | UpgradeLog*.htm 301 | 302 | # SQL Server files 303 | *.mdf 304 | *.ldf 305 | *.ndf 306 | 307 | # Business Intelligence projects 308 | *.rdl.data 309 | *.bim.layout 310 | *.bim_*.settings 311 | 312 | # Microsoft Fakes 313 | FakesAssemblies/ 314 | 315 | # GhostDoc plugin setting file 316 | *.GhostDoc.xml 317 | 318 | # Node.js Tools for Visual Studio 319 | .ntvs_analysis.dat 320 | node_modules/ 321 | 322 | # TypeScript v1 declaration files 323 | typings/ 324 | 325 | # Visual Studio 6 build log 326 | *.plg 327 | 328 | # Visual Studio 6 workspace options file 329 | *.opt 330 | 331 | # Visual Studio 6 auto-generated workspace file (contains which files were open etc.) 332 | *.vbw 333 | 334 | # Visual Studio LightSwitch build output 335 | **/*.HTMLClient/GeneratedArtifacts 336 | **/*.DesktopClient/GeneratedArtifacts 337 | **/*.DesktopClient/ModelManifest.xml 338 | **/*.Server/GeneratedArtifacts 339 | **/*.Server/ModelManifest.xml 340 | _Pvt_Extensions 341 | 342 | # Paket dependency manager 343 | .paket/paket.exe 344 | paket-files/ 345 | 346 | # FAKE - F# Make 347 | .fake/ 348 | 349 | # JetBrains Rider 350 | .idea/ 351 | *.sln.iml 352 | 353 | # CodeRush 354 | .cr/ 355 | 356 | # Python Tools for Visual Studio (PTVS) 357 | __pycache__/ 358 | *.pyc 359 | 360 | # Cake - Uncomment if you are using it 361 | # tools/** 362 | # !tools/packages.config 363 | 364 | # Tabs Studio 365 | *.tss 366 | 367 | # Telerik's JustMock configuration file 368 | *.jmconfig 369 | 370 | # BizTalk build output 371 | *.btp.cs 372 | *.btm.cs 373 | *.odx.cs 374 | *.xsd.cs 375 | 376 | # OpenCover UI analysis results 377 | OpenCover/ 378 | 379 | # Azure Stream Analytics local run output 380 | ASALocalRun/ 381 | 382 | # MSBuild Binary and Structured Log 383 | *.binlog 384 | 385 | log_file.txt 386 | .metadata/ 387 | .mxproject 388 | .settings/ 389 | project.ioc 390 | mx.scratch 391 | *.tilen majerle 392 | 393 | 394 | # Altium 395 | Project outputs* 396 | History/ 397 | *.SchDocPreview 398 | *.$$$Preview 399 | 400 | # VSCode projects 401 | project_vscode_compiled.exe -------------------------------------------------------------------------------- /.gitmodules: -------------------------------------------------------------------------------- 1 | [submodule "third_party/Unity"] 2 | path = third_party/Unity 3 | url = https://github.com/ThrowTheSwitch/Unity.git 4 | [submodule "third_party/sanitizers-cmake"] 5 | path = third_party/sanitizers-cmake 6 | url = https://github.com/arsenm/sanitizers-cmake.git 7 | [submodule "third_party/cmake-modules"] 8 | path = third_party/cmake-modules 9 | url = https://github.com/bilke/cmake-modules.git -------------------------------------------------------------------------------- /.readthedocs.yaml: -------------------------------------------------------------------------------- 1 | version: 2 2 | build: 3 | os: ubuntu-22.04 4 | tools: 5 | python: "3.11" 6 | 7 | # Build documentation in the docs/ directory with Sphinx 8 | sphinx: 9 | configuration: docs/conf.py 10 | 11 | # Python configuration 12 | python: 13 | install: 14 | - requirements: docs/requirements.txt 15 | 16 | formats: 17 | - pdf 18 | - epub 19 | -------------------------------------------------------------------------------- /.vscode/c_cpp_properties.json: -------------------------------------------------------------------------------- 1 | { 2 | "version": 4, 3 | "configurations": [ 4 | { 5 | /* 6 | * Full configuration is provided by CMake plugin for vscode, 7 | * that shall be installed by user 8 | */ 9 | "name": "Win32", 10 | "intelliSenseMode": "${default}", 11 | "configurationProvider": "ms-vscode.cmake-tools" 12 | } 13 | ] 14 | } -------------------------------------------------------------------------------- /.vscode/extensions.json: -------------------------------------------------------------------------------- 1 | { 2 | "recommendations": [ 3 | "ms-vscode.cpptools", 4 | "ms-vscode.cmake-tools", 5 | "twxs.cmake", 6 | ] 7 | } -------------------------------------------------------------------------------- /.vscode/launch.json: -------------------------------------------------------------------------------- 1 | { 2 | "version": "0.2.0", 3 | "configurations": [ 4 | { 5 | /* GDB must in be in the PATH environment */ 6 | "name": "(Windows) Launch", 7 | "type": "cppdbg", 8 | "request": "launch", 9 | "program": "${command:cmake.launchTargetPath}", 10 | "args": [], 11 | "stopAtEntry": false, 12 | "cwd": "${fileDirname}", 13 | "environment": [] 14 | } 15 | ] 16 | } -------------------------------------------------------------------------------- /.vscode/settings.json: -------------------------------------------------------------------------------- 1 | { 2 | "files.associations": { 3 | "lwevt_types.h": "c", 4 | "lwevt_type.h": "c", 5 | "lwevt.h": "c", 6 | "string.h": "c", 7 | "lwevt_opt.h": "c", 8 | "stdatomic.h": "c", 9 | "lwrb.h": "c", 10 | "*.tcc": "c" 11 | }, 12 | "esbonio.sphinx.confDir": "" 13 | } -------------------------------------------------------------------------------- /.vscode/tasks.json: -------------------------------------------------------------------------------- 1 | { 2 | "version": "2.0.0", 3 | "tasks": [ 4 | { 5 | "type": "cppbuild", 6 | "label": "Build project", 7 | "command": "cmake", 8 | "args": ["--build", "${command:cmake.buildDirectory}", "-j", "8"], 9 | "options": { 10 | "cwd": "${workspaceFolder}" 11 | }, 12 | "problemMatcher": ["$gcc"], 13 | "group": { 14 | "kind": "build", 15 | "isDefault": true 16 | } 17 | }, 18 | { 19 | "type": "shell", 20 | "label": "Re-build project", 21 | "command": "cmake", 22 | "args": ["--build", "${command:cmake.buildDirectory}", "--clean-first", "-v", "-j", "8"], 23 | "options": { 24 | "cwd": "${workspaceFolder}" 25 | }, 26 | "problemMatcher": ["$gcc"], 27 | }, 28 | { 29 | "type": "shell", 30 | "label": "Clean project", 31 | "command": "cmake", 32 | "args": ["--build", "${command:cmake.buildDirectory}", "--target", "clean"], 33 | "options": { 34 | "cwd": "${workspaceFolder}" 35 | }, 36 | "problemMatcher": [] 37 | }, 38 | { 39 | "type": "shell", 40 | "label": "Run application", 41 | "command": "${command:cmake.launchTargetPath}", 42 | "args": [], 43 | "problemMatcher": [], 44 | }, 45 | { 46 | "label": "Docs: Install python plugins from requirements.txt file", 47 | "type": "shell", 48 | "command": "python -m pip install -r requirements.txt", 49 | "options": { 50 | "cwd": "${workspaceFolder}/docs" 51 | }, 52 | "problemMatcher": [] 53 | }, 54 | { 55 | "label": "Docs: Generate html", 56 | "type": "shell", 57 | "command": ".\\make html", 58 | "options": { 59 | "cwd": "${workspaceFolder}/docs" 60 | }, 61 | "problemMatcher": [] 62 | }, 63 | { 64 | "label": "Docs: Clean build directory", 65 | "type": "shell", 66 | "command": ".\\make clean", 67 | "options": { 68 | "cwd": "${workspaceFolder}/docs" 69 | }, 70 | "problemMatcher": [] 71 | }, 72 | ] 73 | } -------------------------------------------------------------------------------- /AUTHORS: -------------------------------------------------------------------------------- 1 | Tilen Majerle 2 | Tofik Sonono 3 | Jackistang 4 | Tilen Majerle 5 | Junde Yhi 6 | jnz86 7 | LinJieqiang <517503838@qq.com> 8 | Thomas Devoogdt 9 | Jaedeok Kim -------------------------------------------------------------------------------- /CHANGELOG.md: -------------------------------------------------------------------------------- 1 | # Changelog 2 | 3 | ## Develop 4 | 5 | - Rework library CMake with removed INTERFACE type 6 | 7 | ## v3.2.0 8 | 9 | - Add user argument option 10 | - Improve the variable names to better conform *LLVM* tidy config 11 | 12 | ## v3.1.0 13 | 14 | - Preparation for `v3.1` 15 | - Replace `size_t` with custom defined type `lwrb_sz_t` which matches atomicity requirements 16 | - `lwrb_sz_t` is by default typedef-ed as `unsigned long` 17 | - Prepare `lwrb_write_ex` and `lwrb_read_ex` functions 18 | - Implement `lwrb_write_ex` and `lwrb_read_ex` functions 19 | - Fix `_ex` module throwing an error for Platform.IO 20 | 21 | ## v3.0.0 22 | 23 | - Added macros for optional STDATOMIC. Global `-DLWRB_DISABLE_ATOMIC` macro will disable C11 `` functionality. 24 | - Add `lwrb_move` and `lwrb_overwrite` 25 | - Fix `lwrb_find` which failed to properly search for tokens at corner cases 26 | 27 | ## v3.0.0-RC1 28 | 29 | - Split CMakeLists.txt files between library and executable 30 | - Change license year to 2022 31 | - Update code style with astyle 32 | - Minimum required version is C11, with requirement of `stdatomic.h` library 33 | - Add `.clang-format` draft 34 | 35 | ## v2.0.3 36 | 37 | - Add `library.json` for Platform.IO 38 | 39 | ## v2.0.2 40 | 41 | - Add `volatile` keyword to all local variables to ensure thread safety in highest optimization 42 | - Add local variables for all read and write pointer accesses 43 | - Remove generic `volatile` keyword from func parameter and replace to struct member 44 | 45 | ## v2.0.1 46 | 47 | - Fix wrong check for valid RB instance 48 | - Apply code style settings with Artistic style options 49 | - Add thread safety docs 50 | 51 | ## v2.0.0 52 | 53 | - Break compatibility with previous versions 54 | - Rename function prefixes to `lwrb` instead of `ringbuff` 55 | - Add astyle code syntax correction 56 | 57 | ## v1.3.1 58 | 59 | - Fixed missing `RINGBUFF_VOLATILE` for event callback causes compiler warnings or errors 60 | 61 | ## v1.3.0 62 | 63 | - Added support for events on read/write or reset operation 64 | - Added optional volatile parameter for buffer structure 65 | - Fix bug in skip and advance operation to return actual amount of bytes processed 66 | - Remove `BUF_PREF` parameter and rename with fixed `ringbuff_` prefix for all functions 67 | 68 | ## v1.2.0 69 | 70 | - Added first sphinx documentation 71 | 72 | ## v1.1.0 73 | 74 | - Code optimizations, use pre-increment instead of post 75 | - Another code-style fixes 76 | 77 | ## v1.0.0 78 | 79 | - First stable release 80 | -------------------------------------------------------------------------------- /CMakeLists.txt: -------------------------------------------------------------------------------- 1 | cmake_minimum_required(VERSION 3.22) 2 | 3 | # Setup project 4 | project(LwLibPROJECT C) 5 | 6 | if(NOT PROJECT_IS_TOP_LEVEL) 7 | add_subdirectory(lwrb) 8 | else() 9 | enable_testing() 10 | add_executable(${PROJECT_NAME}) 11 | target_sources(${PROJECT_NAME} PRIVATE 12 | ${CMAKE_CURRENT_LIST_DIR}/dev/main.c 13 | ) 14 | target_include_directories(${PROJECT_NAME} PUBLIC 15 | ${CMAKE_CURRENT_LIST_DIR}/dev 16 | ) 17 | 18 | # Add subdir with lwrb and link to project 19 | set(LWRB_COMPILE_DEFINITIONS LWRB_DEV) 20 | add_subdirectory(lwrb) 21 | target_link_libraries(${PROJECT_NAME} lwrb) 22 | target_link_libraries(${PROJECT_NAME} lwrb_ex) 23 | 24 | # Add compile options to the library, which will propagate options to executable through public link 25 | target_compile_definitions(lwrb PUBLIC WIN32 _DEBUG CONSOLE LWRB_DEV) 26 | target_compile_options(lwrb PUBLIC -Wall -Wextra -Wpedantic) 27 | 28 | # Add test 29 | add_test(NAME Test COMMAND $) 30 | endif() -------------------------------------------------------------------------------- /CMakePresets.json: -------------------------------------------------------------------------------- 1 | { 2 | "version": 3, 3 | "configurePresets": [ 4 | { 5 | "name": "default", 6 | "hidden": true, 7 | "generator": "Ninja", 8 | "binaryDir": "${sourceDir}/build/${presetName}", 9 | "cacheVariables": { 10 | "CMAKE_EXPORT_COMPILE_COMMANDS": "ON" 11 | } 12 | }, 13 | { 14 | "name": "Win32-Debug", 15 | "inherits": "default", 16 | "toolchainFile": "${sourceDir}/cmake/i686-w64-mingw32-gcc.cmake", 17 | "cacheVariables": { 18 | "CMAKE_BUILD_TYPE": "Debug" 19 | } 20 | }, 21 | { 22 | "name": "Win64-Debug", 23 | "inherits": "default", 24 | "toolchainFile": "${sourceDir}/cmake/x86_64-w64-mingw32-gcc.cmake", 25 | "cacheVariables": { 26 | "CMAKE_BUILD_TYPE": "Debug" 27 | } 28 | } 29 | ], 30 | "buildPresets": [ 31 | { 32 | "name": "Win32-Debug", 33 | "configurePreset": "Win32-Debug" 34 | }, 35 | { 36 | "name": "Win64-Debug", 37 | "configurePreset": "Win64-Debug" 38 | } 39 | ] 40 | } -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2024 Tilen MAJERLE 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Lightweight ring buffer manager 2 | 3 | Library provides generic FIFO ring buffer implementation. 4 | 5 |

Read first: Documentation

6 | 7 | ## Features 8 | 9 | * Written in C (C11), compatible with ``size_t`` for size data types 10 | * Platform independent default code - with restrictions for smaller CPU architectures (`< sizeof(size_t)`) 11 | * FIFO (First In First Out) buffer implementation 12 | * No dynamic memory allocation, data is static array 13 | * Uses optimized memory copy instead of loops to read/write data from/to memory 14 | * Thread safe when used as pipe with single write and single read entries - when CPU read/write operation for `size_t` are single instruction (ARM Cortex-M for instance) 15 | * Interrupt safe when used as pipe with single write and single read entries - when CPU read/write operation for `size_t` are single instruction (ARM Cortex-M for instance) 16 | * For CPU systems with smaller architecture than `sizeof(size_t)` (AVR for instance), atomic protection is required for read-write operation of buffer writes 17 | * Suitable for DMA transfers from and to memory with zero-copy overhead between buffer and application memory 18 | * Supports data peek, skip for read and advance for write 19 | * Implements support for event notifications 20 | * User friendly MIT license 21 | 22 | ## Contribute 23 | 24 | Fresh contributions are always welcome. Simple instructions to proceed: 25 | 26 | 1. Fork Github repository 27 | 2. Follow [C style & coding rules](https://github.com/MaJerle/c-code-style) already used in the project 28 | 3. Create a pull request to develop branch with new features or bug fixes 29 | 30 | Alternatively you may: 31 | 32 | 1. Report a bug 33 | 2. Ask for a feature request 34 | -------------------------------------------------------------------------------- /cmake/i686-w64-mingw32-gcc.cmake: -------------------------------------------------------------------------------- 1 | set(CMAKE_SYSTEM_NAME Windows) 2 | 3 | # Some default GCC settings 4 | set(CMAKE_C_COMPILER i686-w64-mingw32-gcc) 5 | set(CMAKE_CXX_COMPILER i686-w64-mingw32-g++) 6 | 7 | set(CMAKE_TRY_COMPILE_TARGET_TYPE STATIC_LIBRARY) 8 | -------------------------------------------------------------------------------- /cmake/x86_64-w64-mingw32-gcc.cmake: -------------------------------------------------------------------------------- 1 | set(CMAKE_SYSTEM_NAME Windows) 2 | 3 | # Some default GCC settings 4 | set(CMAKE_C_COMPILER x86_64-w64-mingw32-gcc) 5 | set(CMAKE_CXX_COMPILER x86_64-w64-mingw32-g++) 6 | 7 | set(CMAKE_TRY_COMPILE_TARGET_TYPE STATIC_LIBRARY) 8 | -------------------------------------------------------------------------------- /dev/main.c: -------------------------------------------------------------------------------- 1 | // lwrb_dev.cpp : Defines the entry point for the console application. 2 | // 3 | 4 | #include 5 | #include 6 | #include "lwrb/lwrb.h" 7 | 8 | /* Create data array and buffer */ 9 | uint8_t lwrb_data[8 + 1]; 10 | lwrb_t buff; 11 | 12 | uint8_t tmp[8]; 13 | 14 | void 15 | my_buff_evt_fn(lwrb_t* buff, lwrb_evt_type_t type, lwrb_sz_t len) { 16 | (void)buff; 17 | (void)len; 18 | switch (type) { 19 | case LWRB_EVT_RESET: printf("[EVT] Buffer reset event!\r\n"); break; 20 | case LWRB_EVT_READ: printf("[EVT] Buffer read event: %d byte(s)!\r\n", (int)len); break; 21 | case LWRB_EVT_WRITE: printf("[EVT] Buffer write event: %d byte(s)!\r\n", (int)len); break; 22 | default: break; 23 | } 24 | } 25 | 26 | int 27 | main(void) { 28 | int retval = 0; 29 | lwrb_sz_t len; 30 | 31 | /* Init buffer */ 32 | lwrb_init(&buff, lwrb_data, sizeof(lwrb_data)); 33 | 34 | printf("Read/Write test\r\n"); 35 | { 36 | uint8_t rw_buff[8]; 37 | 38 | #define RW_TEST(_w_exp_, _r_exp_, _rw_len_, _rw_exp_len_) \ 39 | do { \ 40 | uint8_t is_as_expected = (buff.w_ptr == (_w_exp_) && buff.r_ptr == (_r_exp_) && (_rw_len_) == (_rw_exp_len_)); \ 41 | printf("W ptr: %u, R ptr: %u, R/W len: %u, as_expected: %u\r\n", (unsigned)buff.w_ptr, (unsigned)buff.r_ptr, \ 42 | (unsigned)(_rw_len_), (unsigned)is_as_expected); \ 43 | if (!is_as_expected) { \ 44 | printf("Test failed on line %u", (unsigned)__LINE__); \ 45 | retval = -1; \ 46 | } \ 47 | } while (0) 48 | 49 | lwrb_reset(&buff); 50 | len = lwrb_write(&buff, "abc", 3); /* Write 3 bytes */ 51 | RW_TEST(3, 0, len, 3); 52 | len = lwrb_write(&buff, "abc", 3); /* Write 3 bytes */ 53 | RW_TEST(6, 0, len, 3); 54 | len = lwrb_read(&buff, rw_buff, 3); /* Read 3 bytes */ 55 | RW_TEST(6, 3, len, 3); 56 | len = lwrb_read(&buff, rw_buff, 4); /* Read 4 bytes */ 57 | RW_TEST(6, 6, len, 3); 58 | 59 | len = lwrb_write(&buff, "abc", 3); /* Write 3 bytes -> buffer should go over */ 60 | RW_TEST(0, 6, len, 3); 61 | 62 | #undef RW_TEST 63 | } 64 | 65 | printf("Read/Write extended test\r\n"); 66 | { 67 | uint8_t rw_buff[8]; 68 | lwrb_sz_t written, read; 69 | uint8_t success; 70 | 71 | #define RW_TEST(_w_exp_, _r_exp_, _success_, _rw_len_, _rw_exp_len_) \ 72 | do { \ 73 | uint8_t is_as_expected = (buff.w_ptr == (_w_exp_) && buff.r_ptr == (_r_exp_) && (_rw_len_) == (_rw_exp_len_)); \ 74 | printf("W ptr: %u, R ptr: %u, R/W success: %u, R/W len: %u, as_expected: %u\r\n", (unsigned)buff.w_ptr, \ 75 | (unsigned)buff.r_ptr, (unsigned)(_success_), (unsigned)(_rw_len_), (unsigned)is_as_expected); \ 76 | if (!is_as_expected) { \ 77 | printf("Test failed on line %u", (unsigned)__LINE__); \ 78 | retval = -1; \ 79 | } \ 80 | } while (0) 81 | 82 | lwrb_reset(&buff); 83 | written = 0; 84 | success = lwrb_write_ex(&buff, "abcdefg", 7, &written, LWRB_FLAG_WRITE_ALL); /* Write all bytes */ 85 | RW_TEST(7, 0, success, written, 7); 86 | success = lwrb_read_ex(&buff, rw_buff, 3, &read, LWRB_FLAG_READ_ALL); /* Read 3 bytes only */ 87 | printf("RW FULL READ: %u, as_expected: %u\r\n", (unsigned)success, (unsigned)(success == 1)); 88 | RW_TEST(7, 3, success, written, 7); 89 | 90 | /* This one shall failed, not enough memory available */ 91 | success = lwrb_write_ex(&buff, "abcdefg", 7, &written, LWRB_FLAG_WRITE_ALL); /* Write all bytes */ 92 | printf("RW FULL WRITE: %u, as_expected: %u\r\n", (unsigned)success, (unsigned)(success == 0)); 93 | 94 | /* Read few more bytes to allow full write */ 95 | success = lwrb_read_ex(&buff, rw_buff, 3, &read, LWRB_FLAG_READ_ALL); /* Read 3 bytes only */ 96 | printf("RW FULL READ: %u, as_expected: %u\r\n", (unsigned)success, (unsigned)(success == 1)); 97 | 98 | /* Now it should go through */ 99 | success = lwrb_write_ex(&buff, "abcdefg", 7, &written, LWRB_FLAG_WRITE_ALL); /* Write all bytes */ 100 | printf("RW FULL WRITE: %u, as_expected: %u\r\n", (unsigned)success, (unsigned)(success == 1)); 101 | 102 | #undef RW_TEST 103 | } 104 | 105 | printf("Overwrite test\r\n"); 106 | { 107 | #define OVERWRITE_TEST(_exp_content_, _exp_len_) \ 108 | do { \ 109 | len = lwrb_peek(&buff, 0, tmp, buff.size); \ 110 | uint32_t is_as_expected = (strncmp((_exp_content_), (const void*)tmp, len) == 0 && len == (_exp_len_)); \ 111 | printf("overwrite data read: %.*s, len: %u, as_expected: %u\r\n", (int)len, tmp, (unsigned)len, \ 112 | (unsigned)is_as_expected); \ 113 | if (!is_as_expected) { \ 114 | printf("Test failed on line %u", (unsigned)__LINE__); \ 115 | retval = -1; \ 116 | } \ 117 | } while (0) 118 | 119 | /* Test overwrite */ 120 | lwrb_reset(&buff); 121 | lwrb_write(&buff, "abcdef", 6); /* Initial data */ 122 | OVERWRITE_TEST("abcdef", 6); 123 | 124 | lwrb_overwrite(&buff, "0", 1); 125 | OVERWRITE_TEST("abcdef0", 7); 126 | 127 | lwrb_overwrite(&buff, "1", 1); 128 | OVERWRITE_TEST("abcdef01", 8); 129 | 130 | lwrb_overwrite(&buff, "2", 1); 131 | OVERWRITE_TEST("bcdef012", 8); 132 | 133 | lwrb_overwrite(&buff, "3", 1); 134 | OVERWRITE_TEST("cdef0123", 8); 135 | 136 | lwrb_overwrite(&buff, "4", 1); 137 | OVERWRITE_TEST("def01234", 8); 138 | 139 | lwrb_overwrite(&buff, "5", 1); 140 | OVERWRITE_TEST("ef012345", 8); 141 | 142 | /* Bigger write which will completely change the buffer structure */ 143 | lwrb_overwrite(&buff, "lwrb_new_test_structure", 23); 144 | OVERWRITE_TEST("tructure", 8); 145 | #undef OVERWRITE_TEST 146 | } 147 | 148 | printf("Move test\r\n"); 149 | { 150 | #define MOVE_TEST(_exp_content_, _exp_move_len_, _exp_buff_len_) \ 151 | do { \ 152 | lwrb_sz_t move_len; \ 153 | move_len = lwrb_move(&dst, &src); \ 154 | len = lwrb_peek(&dst, 0, tmp, dst.size); \ 155 | \ 156 | uint32_t is_as_expected = (strncmp((_exp_content_), (const void*)tmp, len) == 0 \ 157 | && move_len == (_exp_move_len_) && len == (_exp_buff_len_)); \ 158 | printf("move data: len: %d, dest data: %.*s, as_expected: %u\r\n", (int)len, (int)len, tmp, \ 159 | (unsigned)is_as_expected); \ 160 | if (!is_as_expected) { \ 161 | printf("Test failed on line %u", (unsigned)__LINE__); \ 162 | retval = -1; \ 163 | } \ 164 | } while (0) 165 | 166 | lwrb_t src, dst; 167 | uint8_t src_data[16], dst_data[8]; 168 | lwrb_init(&src, src_data, sizeof(src_data)); 169 | lwrb_init(&dst, dst_data, sizeof(dst_data)); 170 | 171 | lwrb_reset(&src); 172 | lwrb_reset(&dst); 173 | lwrb_write(&src, "012345", 6); 174 | MOVE_TEST("012345", 6, 6); 175 | 176 | lwrb_reset(&src); 177 | lwrb_reset(&dst); 178 | lwrb_write(&src, "0123456789ABCDEF", 16); 179 | MOVE_TEST("0123456", 7, 7); 180 | 181 | lwrb_reset(&src); 182 | lwrb_reset(&dst); 183 | lwrb_write(&src, "0123456789ABCDEF", 16); 184 | lwrb_write(&dst, "TT", 2); 185 | MOVE_TEST("TT01234", 5, 7); 186 | 187 | #undef MOVE_TEST 188 | } 189 | 190 | (void)len; 191 | 192 | printf("Find test\r\n"); 193 | { 194 | #define FIND_TEST(_bts_, _bts_len_, _start_offset_, _exp_result_) \ 195 | do { \ 196 | lwrb_sz_t found_idx; \ 197 | uint8_t found; \ 198 | found = lwrb_find(&buff, (_bts_), (_bts_len_), (_start_offset_), &found_idx); \ 199 | \ 200 | uint32_t is_as_expected = (!!found == !!(_exp_result_)); \ 201 | printf("Find \"%s\" (len %d), start_offset: %d, found_index: %d; Found: %d; As expected: %d\r\n", (_bts_), \ 202 | (_bts_len_), (_start_offset_), (int)found_idx, (int)found, (int)is_as_expected); \ 203 | if (!is_as_expected) { \ 204 | printf("Test failed on line %u", (unsigned)__LINE__); \ 205 | retval = -1; \ 206 | } \ 207 | } while (0) 208 | 209 | /* Prepare buffer and write data */ 210 | lwrb_reset(&buff); 211 | lwrb_write(&buff, "12345678", 8); 212 | 213 | FIND_TEST("123", 3, 0, 1); /* Must find it */ 214 | FIND_TEST("456", 3, 0, 1); /* Must find it */ 215 | FIND_TEST("123", 3, 1, 0); /* Must not find it - start offset is later */ 216 | FIND_TEST("678", 3, 0, 1); 217 | 218 | /* Restart by setting write and read as empty with offset */ 219 | buff.w_ptr = 6; 220 | buff.r_ptr = 6; 221 | lwrb_write(&buff, "12345678", 8); 222 | 223 | FIND_TEST("123", 3, 0, 1); /* Must find it */ 224 | FIND_TEST("456", 3, 0, 1); /* Must find it */ 225 | FIND_TEST("123", 3, 1, 0); /* Must not find it - start offset is later */ 226 | 227 | /* Restart by setting write and read as empty with offset */ 228 | /* This should generate data for search in overflow mode */ 229 | buff.w_ptr = 8; 230 | buff.r_ptr = 8; 231 | lwrb_write(&buff, "12345678", 8); 232 | 233 | FIND_TEST("1234", 3, 0, 1); /* Must find it */ 234 | FIND_TEST("4567", 3, 0, 1); /* Must find it */ 235 | FIND_TEST("1234", 3, 1, 0); /* Must not find it - start offset is later */ 236 | 237 | #undef FIND_TEST 238 | } 239 | 240 | printf("Done!\r\n"); 241 | return retval; 242 | } 243 | -------------------------------------------------------------------------------- /docs/Makefile: -------------------------------------------------------------------------------- 1 | # Minimal makefile for Sphinx documentation 2 | # 3 | 4 | # You can set these variables from the command line, and also 5 | # from the environment for the first two. 6 | SPHINXOPTS ?= 7 | SPHINXBUILD ?= sphinx-build 8 | SOURCEDIR = . 9 | BUILDDIR = _build 10 | 11 | # Put it first so that "make" without argument is like "make help". 12 | help: 13 | @$(SPHINXBUILD) -M help "$(SOURCEDIR)" "$(BUILDDIR)" $(SPHINXOPTS) $(O) 14 | 15 | .PHONY: help Makefile 16 | 17 | # Catch-all target: route all unknown targets to Sphinx using the new 18 | # "make mode" option. $(O) is meant as a shortcut for $(SPHINXOPTS). 19 | %: Makefile 20 | @$(SPHINXBUILD) -M $@ "$(SOURCEDIR)" "$(BUILDDIR)" $(SPHINXOPTS) $(O) 21 | -------------------------------------------------------------------------------- /docs/api-reference/index.rst: -------------------------------------------------------------------------------- 1 | .. _api_reference: 2 | 3 | API reference 4 | ============= 5 | 6 | List of all the modules: 7 | 8 | .. toctree:: 9 | :maxdepth: 2 10 | 11 | lwrb -------------------------------------------------------------------------------- /docs/api-reference/lwrb.rst: -------------------------------------------------------------------------------- 1 | .. _api_lwrb: 2 | 3 | LwRB 4 | ==== 5 | 6 | .. doxygengroup:: LWRB -------------------------------------------------------------------------------- /docs/authors/index.rst: -------------------------------------------------------------------------------- 1 | .. _authors: 2 | 3 | Authors 4 | ======= 5 | 6 | List of authors and contributors to the library 7 | 8 | .. literalinclude:: ../../AUTHORS -------------------------------------------------------------------------------- /docs/changelog/index.rst: -------------------------------------------------------------------------------- 1 | .. _changelof: 2 | 3 | Changelog 4 | ========= 5 | 6 | .. literalinclude:: ../../CHANGELOG.md 7 | -------------------------------------------------------------------------------- /docs/conf.py: -------------------------------------------------------------------------------- 1 | # Configuration file for the Sphinx documentation builder. 2 | # 3 | # This file only contains a selection of the most common options. For a full 4 | # list see the documentation: 5 | # https://www.sphinx-doc.org/en/master/usage/configuration.html 6 | 7 | # -- Path setup -------------------------------------------------------------- 8 | 9 | # If extensions (or modules to document with autodoc) are in another directory, 10 | # add these directories to sys.path here. If the directory is relative to the 11 | # documentation root, use os.path.abspath to make it absolute, like shown here. 12 | # 13 | # import os 14 | # import sys 15 | # sys.path.insert(0, os.path.abspath('.')) 16 | from sphinx.builders.html import StandaloneHTMLBuilder 17 | import subprocess, os 18 | 19 | # Run doxygen first 20 | # read_the_docs_build = os.environ.get('READTHEDOCS', None) == 'True' 21 | # if read_the_docs_build: 22 | subprocess.call('doxygen doxyfile.doxy', shell=True) 23 | # -- Project information ----------------------------------------------------- 24 | 25 | project = 'LwRB' 26 | copyright = '2023, Tilen MAJERLE' 27 | author = 'Tilen MAJERLE' 28 | 29 | # Try to get branch at which this is running 30 | # and try to determine which version to display in sphinx 31 | # Version is using git tag if on master/main or "latest-develop" if on develop branch 32 | version = '' 33 | git_branch = '' 34 | 35 | def cmd_exec_print(t): 36 | print("cmd > ", t, "\n", os.popen(t).read().strip(), "\n") 37 | 38 | # Print demo data here 39 | cmd_exec_print('git branch') 40 | cmd_exec_print('git describe') 41 | cmd_exec_print('git describe --tags') 42 | cmd_exec_print('git describe --tags --abbrev=0') 43 | cmd_exec_print('git describe --tags --abbrev=1') 44 | 45 | # Get current branch 46 | res = os.popen('git branch').read().strip() 47 | for line in res.split("\n"): 48 | if line[0] == '*': 49 | git_branch = line[1:].strip() 50 | 51 | # Decision for display version 52 | git_branch = git_branch.replace('(HEAD detached at ', '').replace(')', '') 53 | if git_branch.find('master') >= 0 or git_branch.find('main') >= 0: 54 | #version = os.popen('git describe --tags --abbrev=0').read().strip() 55 | version = 'latest-stable' 56 | elif git_branch.find('develop-') >= 0 or git_branch.find('develop/') >= 0: 57 | version = 'branch-' + git_branch 58 | elif git_branch == 'develop' or git_branch == 'origin/develop': 59 | version = 'latest-develop' 60 | else: 61 | version = os.popen('git describe --tags --abbrev=0').read().strip() 62 | 63 | # For debugging purpose only 64 | print("GIT BRANCH: " + git_branch) 65 | print("PROJ VERSION: " + version) 66 | 67 | # -- General configuration --------------------------------------------------- 68 | 69 | # Add any Sphinx extension module names here, as strings. They can be 70 | # extensions coming with Sphinx (named 'sphinx.ext.*') or your custom 71 | # ones. 72 | extensions = [ 73 | 'sphinx.ext.autodoc', 74 | 'sphinx.ext.intersphinx', 75 | 'sphinx.ext.autosectionlabel', 76 | 'sphinx.ext.todo', 77 | 'sphinx.ext.coverage', 78 | 'sphinx.ext.mathjax', 79 | 'sphinx.ext.ifconfig', 80 | 'sphinx.ext.viewcode', 81 | 'sphinx_sitemap', 82 | 83 | 'breathe', 84 | ] 85 | 86 | # Add any paths that contain templates here, relative to this directory. 87 | templates_path = ['templates'] 88 | 89 | # List of patterns, relative to source directory, that match files and 90 | # directories to ignore when looking for source files. 91 | # This pattern also affects html_static_path and html_extra_path. 92 | exclude_patterns = ['_build', 'Thumbs.db', '.DS_Store'] 93 | 94 | highlight_language = 'c' 95 | 96 | # -- Options for HTML output ------------------------------------------------- 97 | 98 | # The theme to use for HTML and HTML Help pages. See the documentation for 99 | # a list of builtin themes. 100 | # 101 | html_theme = 'sphinx_rtd_theme' 102 | html_theme_options = { 103 | 'canonical_url': '', 104 | 'analytics_id': '', # Provided by Google in your dashboard 105 | 'display_version': True, 106 | 'prev_next_buttons_location': 'bottom', 107 | 'style_external_links': False, 108 | 109 | 'logo_only': False, 110 | 111 | # Toc options 112 | 'collapse_navigation': True, 113 | 'sticky_navigation': True, 114 | 'navigation_depth': 4, 115 | 'includehidden': True, 116 | 'titles_only': False 117 | } 118 | html_logo = 'static/images/logo.svg' 119 | github_url = 'https://github.com/MaJerle/lwrb' 120 | html_baseurl = 'https://docs.majerle.eu/projects/lwrb/' 121 | 122 | # Add any paths that contain custom static files (such as style sheets) here, 123 | # relative to this directory. They are copied after the builtin static files, 124 | # so a file named "default.css" will overwrite the builtin "default.css". 125 | html_static_path = ['static'] 126 | html_css_files = [ 127 | 'css/common.css', 128 | 'css/custom.css', 129 | 'https://cdnjs.cloudflare.com/ajax/libs/font-awesome/5.15.1/css/all.min.css', 130 | ] 131 | html_js_files = [ 132 | '' 133 | ] 134 | 135 | # Master index file 136 | master_doc = 'index' 137 | 138 | # --- Breathe configuration ----------------------------------------------------- 139 | breathe_projects = { 140 | "lwrb": "_build/xml/" 141 | } 142 | breathe_default_project = "lwrb" 143 | breathe_default_members = ('members', 'undoc-members') 144 | breathe_show_enumvalue_initializer = True -------------------------------------------------------------------------------- /docs/examples_src/example_advance_1.c: -------------------------------------------------------------------------------- 1 | /* Declare rb instance & raw data */ 2 | lwrb_t buff; 3 | uint8_t buff_data[8]; 4 | 5 | size_t len; 6 | uint8_t* data; 7 | 8 | /* Initialize buffer, use buff_data as data array */ 9 | lwrb_init(&buff, buff_data, sizeof(buff_data)); 10 | 11 | /* Use write, read operations, process data */ 12 | /* ... */ 13 | 14 | /* IMAGE PART A */ 15 | 16 | /* At this stage, we have buffer as on image above */ 17 | /* R = 4, W = 4, buffer is considered empty */ 18 | 19 | /* Get length of linear memory at write pointer */ 20 | /* Function returns 4 as we can write 4 bytes to buffer in sequence */ 21 | /* When function returns 0, there is no memory available in the buffer for write anymore */ 22 | if ((len = lwrb_get_linear_block_write_length(&buff)) > 0) { 23 | /* Get pointer to first element in linear block at write address */ 24 | /* Function returns &buff_data[4] */ 25 | data = lwrb_get_linear_block_write_address(&buff); 26 | 27 | /* Receive data via DMA and wait to finish (for sake of example) */ 28 | /* Any other hardware may directly write to data array */ 29 | /* Data array has len bytes length */ 30 | /* Or use memcpy(data, my_array, len); */ 31 | receive_data(data, len); 32 | 33 | /* Now advance buffer for written bytes to buffer = move write pointer */ 34 | /* Write pointer is moved for len bytes */ 35 | lwrb_advance(&buff, len); 36 | 37 | /* Now W points to top of buffer, W = 0 */ 38 | /* At this point, we are at image part B */ 39 | } 40 | 41 | /* IMAGE PART B */ 42 | 43 | /* Get length of linear memory at write pointer */ 44 | /* Function returns 3 as we can write 3 bytes to buffer in sequence */ 45 | /* When function returns 0, there is no memory available in the buffer for write anymore */ 46 | if ((len = lwrb_get_linear_block_write_length(&buff)) > 0) { 47 | /* Get pointer to first element in linear block at write address */ 48 | /* Function returns &buff_data[0] */ 49 | data = lwrb_get_linear_block_write_address(&buff); 50 | 51 | /* Receive data via DMA and wait to finish (for sake of example) */ 52 | /* Any other hardware may directly write to data array */ 53 | /* Data array has len bytes length */ 54 | /* Or use memcpy(data, my_array, len); */ 55 | receive_data(data, len); 56 | 57 | /* Now advance buffer for written bytes to buffer = move write pointer */ 58 | /* Write pointer is moved for len bytes */ 59 | lwrb_advance(&buff, len); 60 | 61 | /* Now W points to 3, R points to 4, that is R == W + 1 and buffer is now full */ 62 | /* At this point, we are at image part C */ 63 | } 64 | 65 | /* IMAGE PART C */ 66 | 67 | /* Buffer is considered full as R == W + 1 */ -------------------------------------------------------------------------------- /docs/examples_src/example_advance_2.c: -------------------------------------------------------------------------------- 1 | #include "lwrb/lwrb.h" 2 | 3 | /* Declare rb instance & raw data */ 4 | lwrb_t buff; 5 | uint8_t buff_data[8]; 6 | 7 | /* Application variables */ 8 | uint8_t data[2]; 9 | 10 | /* Application code ... */ 11 | lwrb_init(&buff, buff_data, sizeof(buff_data)); /* Initialize buffer */ 12 | 13 | /* Write 4 bytes of data */ 14 | lwrb_write(&buff, "0123", 4); 15 | 16 | /* Print number of bytes in buffer */ 17 | printf("Bytes in buffer: %d\r\n", (int)lwrb_get_full(&buff)); 18 | 19 | /* Will print "4" */ -------------------------------------------------------------------------------- /docs/examples_src/example_dma_skip.c: -------------------------------------------------------------------------------- 1 | /* Declare rb instance & raw data */ 2 | lwrb_t buff; 3 | uint8_t buff_data[8]; 4 | 5 | /* Working data length */ 6 | volatile size_t len; 7 | 8 | /* Send data function */ 9 | void send_data(void); 10 | 11 | int 12 | main(void) { 13 | /* Initialize buffer */ 14 | lwrb_init(&buff, buff_data, sizeof(buff_data)); 15 | 16 | /* Write 4 bytes of data */ 17 | lwrb_write(&buff, "0123", 4); 18 | 19 | /* Send data over DMA */ 20 | send_data(); 21 | 22 | while (1); 23 | } 24 | 25 | /* Send data over DMA */ 26 | void 27 | send_data(void) { 28 | /* If len > 0, DMA transfer is on-going */ 29 | if (len > 0) { 30 | return; 31 | } 32 | 33 | /* Get maximal length of buffer to read data as linear memory */ 34 | len = lwrb_get_linear_block_read_length(&buff); 35 | if (len > 0) { 36 | /* Get pointer to read memory */ 37 | uint8_t* data = lwrb_get_linear_block_read_address(&buff); 38 | 39 | /* Start DMA transfer */ 40 | start_dma_transfer(data, len); 41 | } 42 | 43 | /* Function does not wait for transfer to finish */ 44 | } 45 | 46 | /* Interrupt handler */ 47 | /* Called on DMA transfer finish */ 48 | void 49 | DMA_Interrupt_handler(void) { 50 | /* Transfer finished */ 51 | if (len > 0) { 52 | /* Now skip the data (move read pointer) as they were successfully transferred over DMA */ 53 | lwrb_skip(&buff, len); 54 | 55 | /* Reset length = DMA is not active */ 56 | len = 0; 57 | 58 | /* Try to send more */ 59 | send_data(); 60 | } 61 | } 62 | -------------------------------------------------------------------------------- /docs/examples_src/example_events.c: -------------------------------------------------------------------------------- 1 | /** 2 | * \brief Buffer event function 3 | */ 4 | void 5 | my_buff_evt_fn(lwrb_t* buff, lwrb_evt_type_t type, size_t len) { 6 | switch (type) { 7 | case LWRB_EVT_RESET: 8 | printf("[EVT] Buffer reset event!\r\n"); 9 | break; 10 | case LWRB_EVT_READ: 11 | printf("[EVT] Buffer read event: %d byte(s)!\r\n", (int)len); 12 | break; 13 | case LWRB_EVT_WRITE: 14 | printf("[EVT] Buffer write event: %d byte(s)!\r\n", (int)len); 15 | break; 16 | default: break; 17 | } 18 | } 19 | 20 | /* Later in the code... */ 21 | lwrb_t buff; 22 | uint8_t buff_data[8]; 23 | 24 | /* Init buffer and set event function */ 25 | lwrb_init(&buff, buff_data, sizeof(buff_data)); 26 | lwrb_set_evt_fn(&buff, my_buff_evt_fn); 27 | -------------------------------------------------------------------------------- /docs/examples_src/example_index.c: -------------------------------------------------------------------------------- 1 | /* Declare rb instance & raw data */ 2 | lwrb_t buff; 3 | uint8_t buff_data[8]; 4 | 5 | /* Application variables */ 6 | uint8_t data[2]; 7 | size_t len; 8 | 9 | /* Application code ... */ 10 | lwrb_init(&buff, buff_data, sizeof(buff_data)); /* Initialize buffer */ 11 | 12 | /* Write 4 bytes of data */ 13 | lwrb_write(&buff, "0123", 4); 14 | 15 | /* Try to read buffer */ 16 | /* len holds number of bytes read */ 17 | /* Read until len == 0, when buffer is empty */ 18 | while ((len = lwrb_read(&buff, data, sizeof(data))) > 0) { 19 | printf("Successfully read %d bytes\r\n", (int)len); 20 | } -------------------------------------------------------------------------------- /docs/examples_src/example_minimal.c: -------------------------------------------------------------------------------- 1 | #include "lwrb/lwrb.h" 2 | 3 | /* Declare rb instance & raw data */ 4 | lwrb_t buff; 5 | uint8_t buff_data[8]; 6 | 7 | /* Application code ... */ 8 | lwrb_init(&buff, buff_data, sizeof(buff_data)); /* Initialize buffer */ 9 | 10 | /* Write 4 bytes of data */ 11 | lwrb_write(&buff, "0123", 4); 12 | 13 | /* Print number of bytes in buffer */ 14 | printf("Bytes in buffer: %d\r\n", (int)lwrb_get_full(&buff)); 15 | /* Will print "4" */ 16 | 17 | /* Now let's read */ 18 | uint8_t data[8]; /* Application working data */ 19 | size_t len; 20 | 21 | /* Read from buffer, will return number of bytes read */ 22 | len = lwrb_read(&buff, data, sizeof(data)); 23 | printf("Number of bytes read: %d\r\n", (int)len); 24 | 25 | /* Data is now available in the "data" variable */ 26 | -------------------------------------------------------------------------------- /docs/examples_src/example_skip_1.c: -------------------------------------------------------------------------------- 1 | #include "lwrb/lwrb.h" 2 | 3 | /* Declare rb instance & raw data */ 4 | lwrb_t buff; 5 | uint8_t buff_data[8]; 6 | 7 | size_t len; 8 | uint8_t* data; 9 | 10 | /* Initialize buffer, use buff_data as data array */ 11 | lwrb_init(&buff, buff_data, sizeof(buff_data)); 12 | 13 | /* Use write, read operations, process data */ 14 | /* ... */ 15 | 16 | /* IMAGE PART A */ 17 | 18 | /* At this stage, we have buffer as on image above */ 19 | /* R = 5, W = 4, buffer is considered full */ 20 | 21 | /* Get length of linear memory at read pointer */ 22 | /* Function returns 3 as we can read 3 bytes from buffer in sequence */ 23 | /* When function returns 0, there is no memory available in the buffer for read anymore */ 24 | if ((len = lwrb_get_linear_block_read_length(&buff)) > 0) { 25 | /* Get pointer to first element in linear block at read address */ 26 | /* Function returns &buff_data[5] */ 27 | data = lwrb_get_linear_block_read_address(&buff); 28 | 29 | /* Send data via DMA and wait to finish (for sake of example) */ 30 | send_data(data, len); 31 | 32 | /* Now skip sent bytes from buffer = move read pointer */ 33 | lwrb_skip(&buff, len); 34 | 35 | /* Now R points to top of buffer, R = 0 */ 36 | /* At this point, we are at image part B */ 37 | } 38 | 39 | /* IMAGE PART B */ 40 | 41 | /* Get length of linear memory at read pointer */ 42 | /* Function returns 4 as we can read 4 bytes from buffer in sequence */ 43 | /* When function returns 0, there is no memory available in the buffer for read anymore */ 44 | if ((len = lwrb_get_linear_block_read_length(&buff)) > 0) { 45 | /* Get pointer to first element in linear block at read address */ 46 | /* Function returns &buff_data[0] */ 47 | data = lwrb_get_linear_block_read_address(&buff); 48 | 49 | /* Send data via DMA and wait to finish (for sake of example) */ 50 | send_data(data, len); 51 | 52 | /* Now skip sent bytes from buffer = move read pointer */ 53 | /* Read pointer is moved for len bytes */ 54 | lwrb_skip(&buff, len); 55 | 56 | /* Now R points to 4, that is R == W and buffer is now empty */ 57 | /* At this point, we are at image part C */ 58 | } 59 | 60 | /* IMAGE PART C */ 61 | 62 | /* Buffer is considered empty as R == W */ -------------------------------------------------------------------------------- /docs/examples_src/example_skip_2.c: -------------------------------------------------------------------------------- 1 | /* Initialization part skipped */ 2 | 3 | /* Get length of linear memory at read pointer */ 4 | /* When function returns 0, there is no memory 5 | available in the buffer for read anymore */ 6 | while ((len = lwrb_get_linear_block_read_length(&buff)) > 0) { 7 | /* Get pointer to first element in linear block at read address */ 8 | data = lwrb_get_linear_block_read_address(&buff); 9 | 10 | /* If max length needs to be considered */ 11 | /* simply decrease it and use smaller len on skip function */ 12 | if (len > max_len) { 13 | len = max_len; 14 | } 15 | 16 | /* Send data via DMA and wait to finish (for sake of example) */ 17 | send_data(data, len); 18 | 19 | /* Now skip sent bytes from buffer = move read pointer */ 20 | lwrb_skip(&buff, len); 21 | } -------------------------------------------------------------------------------- /docs/examples_src/example_thread_safety.c: -------------------------------------------------------------------------------- 1 | /* Declare variables */ 2 | lwrb_t rb; 3 | 4 | /* 2 mutexes, one for write operations, 5 | one for read operations */ 6 | mutex_t m_w, m_r; 7 | 8 | /* 4 threads below, 2 for write, 2 for read */ 9 | void 10 | thread_write_1(void* arg) { 11 | /* Use write mutex */ 12 | while (1) { 13 | mutex_get(&m_w); 14 | lwrb_write(&rb, ...); 15 | mutex_give(&m_w); 16 | } 17 | } 18 | 19 | void 20 | thread_write_2(void* arg) { 21 | /* Use write mutex */ 22 | while (1) { 23 | mutex_get(&m_w); 24 | lwrb_write(&rb, ...); 25 | mutex_give(&m_w); 26 | } 27 | } 28 | 29 | void 30 | thread_read_1(void* arg) { 31 | /* Use read mutex */ 32 | while (1) { 33 | mutex_get(&m_r); 34 | lwrb_read(&rb, ...); 35 | mutex_give(&m_r); 36 | } 37 | } 38 | 39 | void 40 | thread_read_2(void* arg) { 41 | /* Use read mutex */ 42 | while (1) { 43 | mutex_get(&m_r); 44 | lwrb_read(&rb, ...); 45 | mutex_give(&m_r); 46 | } 47 | } -------------------------------------------------------------------------------- /docs/examples_src/example_tt_buff_size.c: -------------------------------------------------------------------------------- 1 | #include "lwrb/lwrb.h" 2 | 3 | /* Number of data blocks to write */ 4 | #define N 3 5 | 6 | /* Create custom data structure */ 7 | /* Data is array of 2 32-bit words, 8-bytes */ 8 | uint32_t d[2]; 9 | 10 | /* Create buffer structures */ 11 | lwrb_t buff_1; 12 | lwrb_t buff_2; 13 | 14 | /* Create data for buffers. Use sizeof structure, 15 | multiplied by N (for N instances) */ 16 | /* Buffer with + 1 bytes bigger memory */ 17 | uint8_t buff_data_1[sizeof(d) * N + 1]; 18 | /* Buffer without + 1 at the end */ 19 | uint8_t buff_data_2[sizeof(d) * N]; 20 | 21 | /* Write result values */ 22 | size_t len_1; 23 | size_t len_2; 24 | 25 | /* Initialize buffers */ 26 | lwrb_init(&buff_1, buff_data_1, sizeof(buff_data_1)); 27 | lwrb_init(&buff_2, buff_data_2, sizeof(buff_data_2)); 28 | 29 | /* Write data to buffer */ 30 | for (size_t i = 0; i < N; ++i) { 31 | /* Prepare data */ 32 | d.a = i; 33 | d.b = i * 2; 34 | 35 | /* Write data to both buffers, memory copy from d to buffer */ 36 | len_1 = lwrb_write(&buff_1, d, sizeof(d)); 37 | len_2 = lwrb_write(&buff_2, d, sizeof(d)); 38 | 39 | /* Print results */ 40 | printf("Write buffer 1: %d/%d bytes; buffer 2: %d/%d\r\n", 41 | (int)len_1, (int)sizeof(d), 42 | (int)len_2, (int)sizeof(d)); 43 | } -------------------------------------------------------------------------------- /docs/examples_src/example_tt_buff_size_log.c: -------------------------------------------------------------------------------- 1 | Write: buffer 1: 8/8; buffer 2: 8/8 2 | Write: buffer 1: 8/8; buffer 2: 8/8 3 | Write: buffer 1: 8/8; buffer 2: 7/8 <-- See here --> -------------------------------------------------------------------------------- /docs/get-started/index.rst: -------------------------------------------------------------------------------- 1 | .. _getting_started: 2 | 3 | Getting started 4 | =============== 5 | 6 | Getting started may be the most challenging part of every new library. 7 | This guide is describing how to start with the library quickly and effectively 8 | 9 | .. _download_library: 10 | 11 | Download library 12 | ^^^^^^^^^^^^^^^^ 13 | 14 | Library is primarly hosted on `Github `_. 15 | 16 | You can get it by: 17 | 18 | * Downloading latest release from `releases area `_ on Github 19 | * Cloning ``main`` branch for latest stable version 20 | * Cloning ``develop`` branch for latest development 21 | 22 | Download from releases 23 | ********************** 24 | 25 | All releases are available on Github `releases area `_. 26 | 27 | Clone from Github 28 | ***************** 29 | 30 | First-time clone 31 | """""""""""""""" 32 | 33 | This is used when you do not have yet local copy on your machine. 34 | 35 | * Make sure ``git`` is installed. 36 | * Open console and navigate to path in the system to clone repository to. Use command ``cd your_path`` 37 | * Clone repository with one of available options below 38 | 39 | * Run ``git clone --recurse-submodules https://github.com/MaJerle/lwrb`` command to clone entire repository, including submodules 40 | * Run ``git clone --recurse-submodules --branch develop https://github.com/MaJerle/lwrb`` to clone `development` branch, including submodules 41 | * Run ``git clone --recurse-submodules --branch main https://github.com/MaJerle/lwrb`` to clone `latest stable` branch, including submodules 42 | 43 | * Navigate to ``examples`` directory and run favourite example 44 | 45 | Update cloned to latest version 46 | """"""""""""""""""""""""""""""" 47 | 48 | * Open console and navigate to path in the system where your repository is located. Use command ``cd your_path`` 49 | * Run ``git pull origin main`` command to get latest changes on ``main`` branch 50 | * Run ``git pull origin develop`` command to get latest changes on ``develop`` branch 51 | * Run ``git submodule update --init --remote`` to update submodules to latest version 52 | 53 | .. note:: 54 | This is preferred option to use when you want to evaluate library and run prepared examples. 55 | Repository consists of multiple submodules which can be automatically downloaded when cloning and pulling changes from root repository. 56 | 57 | Add library to project 58 | ^^^^^^^^^^^^^^^^^^^^^^ 59 | 60 | At this point it is assumed that you have successfully download library, either with ``git clone`` command or with manual download from the library releases page. 61 | Next step is to add the library to the project, by means of source files to compiler inputs and header files in search path. 62 | 63 | *CMake* is the main supported build system. Package comes with the ``CMakeLists.txt`` and ``library.cmake`` files, both located in the ``lwrb`` directory: 64 | 65 | * ``library.cmake``: It is a fully configured set of variables and with library definition. User can include this file to the project file with ``include(path/to/library.cmake)`` and then manually use the variables provided by the file, such as list of source files, include paths or necessary compiler definitions. It is up to the user to properly use the this file on its own. 66 | * ``CMakeLists.txt``: It is a wrapper-only file and includes ``library.cmake`` file. It is used for when user wants to include the library to the main project by simply calling *CMake* ``add_subdirectory`` command, followed by ``target_link_libraries`` to link external library to the final project. 67 | 68 | .. tip:: 69 | Open ``library.cmake`` and analyze the provided information. Among variables, you can also find list of all possible exposed libraries for the user. 70 | 71 | If you do not use the *CMake*, you can do the following: 72 | 73 | * Copy ``lwrb`` folder to your project, it contains library files 74 | * Add ``lwrb/src/include`` folder to `include path` of your toolchain. This is where `C/C++` compiler can find the files during compilation process. Usually using ``-I`` flag 75 | * Add source files from ``lwrb/src/`` folder to toolchain build. These files are built by `C/C++` compilery 76 | * Build the project 77 | 78 | Minimal example code 79 | ^^^^^^^^^^^^^^^^^^^^ 80 | 81 | To verify proper library setup, minimal example has been prepared. 82 | Run it in your main application file to verify its proper execution 83 | 84 | .. literalinclude:: ../examples_src/example_minimal.c 85 | :language: c 86 | :linenos: 87 | :caption: Absolute minimum example -------------------------------------------------------------------------------- /docs/index.rst: -------------------------------------------------------------------------------- 1 | LwRB |version| documentation 2 | ============================ 3 | 4 | Welcome to the documentation for version |version|. 5 | 6 | LwRB is a generic *FIFO* (First In; First Out) buffer library optimized for embedded systems. 7 | 8 | .. image:: static/images/logo.svg 9 | :align: center 10 | 11 | .. rst-class:: center 12 | .. rst-class:: index_links 13 | 14 | :ref:`download_library` :ref:`getting_started` `Open Github `_ `Donate `_ 15 | 16 | Features 17 | ^^^^^^^^ 18 | 19 | * Written in C (C11), compatible with ``size_t`` for size data types 20 | * Platform independent, no architecture specific code 21 | * FIFO (First In First Out) buffer implementation 22 | * No dynamic memory allocation, data is static array 23 | * Uses optimized memory copy instead of loops to read/write data from/to memory 24 | * Thread safe when used as pipe with single write and single read entries 25 | * Interrupt safe when used as pipe with single write and single read entries 26 | * Suitable for DMA transfers from and to memory with zero-copy overhead between buffer and application memory 27 | * Supports data peek, skip for read and advance for write 28 | * Implements support for event notifications 29 | * User friendly MIT license 30 | 31 | Requirements 32 | ^^^^^^^^^^^^ 33 | 34 | * C compiler 35 | * Less than ``1kB`` of non-volatile memory 36 | 37 | Contribute 38 | ^^^^^^^^^^ 39 | 40 | Fresh contributions are always welcome. Simple instructions to proceed: 41 | 42 | #. Fork Github repository 43 | #. Respect `C style & coding rules `_ used by the library 44 | #. Create a pull request to ``develop`` branch with new features or bug fixes 45 | 46 | Alternatively you may: 47 | 48 | #. Report a bug 49 | #. Ask for a feature request 50 | 51 | Example code 52 | ^^^^^^^^^^^^ 53 | 54 | Minimalistic example code to read and write data to buffer 55 | 56 | .. literalinclude:: examples_src/example_index.c 57 | :language: c 58 | :linenos: 59 | :caption: Example code 60 | 61 | License 62 | ^^^^^^^ 63 | 64 | .. literalinclude:: ../LICENSE 65 | 66 | Table of contents 67 | ^^^^^^^^^^^^^^^^^ 68 | 69 | .. toctree:: 70 | :maxdepth: 2 71 | :caption: Contents 72 | 73 | self 74 | get-started/index 75 | user-manual/index 76 | tips-tricks/index 77 | api-reference/index 78 | changelog/index 79 | authors/index 80 | 81 | .. toctree:: 82 | :maxdepth: 2 83 | :caption: Other projects 84 | :hidden: 85 | 86 | LwBTN - Button manager 87 | LwDTC - DateTimeCron 88 | LwESP - ESP-AT library 89 | LwEVT - Event manager 90 | LwGPS - GPS NMEA parser 91 | LwCELL - Cellular modem host AT library 92 | LwJSON - JSON parser 93 | LwMEM - Memory manager 94 | LwOW - OneWire with UART 95 | LwPKT - Packet protocol 96 | LwPRINTF - Printf 97 | LwRB - Ring buffer 98 | LwSHELL - Shell 99 | LwUTIL - Utility functions 100 | LwWDG - RTOS task watchdog -------------------------------------------------------------------------------- /docs/make.bat: -------------------------------------------------------------------------------- 1 | @ECHO OFF 2 | 3 | pushd %~dp0 4 | 5 | REM Command file for Sphinx documentation 6 | 7 | if "%SPHINXBUILD%" == "" ( 8 | set SPHINXBUILD=sphinx-build 9 | ) 10 | set SOURCEDIR=. 11 | set BUILDDIR=_build 12 | 13 | if "%1" == "" goto help 14 | 15 | %SPHINXBUILD% >NUL 2>NUL 16 | if errorlevel 9009 ( 17 | echo. 18 | echo.The 'sphinx-build' command was not found. Make sure you have Sphinx 19 | echo.installed, then set the SPHINXBUILD environment variable to point 20 | echo.to the full path of the 'sphinx-build' executable. Alternatively you 21 | echo.may add the Sphinx directory to PATH. 22 | echo. 23 | echo.If you don't have Sphinx installed, grab it from 24 | echo.http://sphinx-doc.org/ 25 | exit /b 1 26 | ) 27 | 28 | %SPHINXBUILD% -M %1 %SOURCEDIR% %BUILDDIR% %SPHINXOPTS% %O% 29 | goto end 30 | 31 | :help 32 | %SPHINXBUILD% -M help %SOURCEDIR% %BUILDDIR% %SPHINXOPTS% %O% 33 | 34 | :end 35 | popd 36 | -------------------------------------------------------------------------------- /docs/requirements.txt: -------------------------------------------------------------------------------- 1 | sphinx>=3.5.1 2 | breathe>=4.9.1 3 | urllib3==1.26.18 4 | docutils==0.16 5 | colorama 6 | sphinx_rtd_theme>=1.0.0 7 | sphinx-tabs 8 | sphinxcontrib-svg2pdfconverter 9 | sphinx-sitemap 10 | -------------------------------------------------------------------------------- /docs/static/css/common.css: -------------------------------------------------------------------------------- 1 | /* Center aligned text */ 2 | .center { 3 | text-align: center; 4 | } 5 | 6 | /* Paragraph with main links on index page */ 7 | .index-links { 8 | text-align: center; 9 | margin-top: 10px; 10 | } 11 | .index-links a { 12 | display: inline-block; 13 | border: 1px solid #0E4263; 14 | padding: 5px 20px; 15 | margin: 2px 5px; 16 | background: #2980B9; 17 | border-radius: 4px; 18 | color: #FFFFFF; 19 | } 20 | .index-links a:hover, .index-links a:active { 21 | background: #0E4263; 22 | } 23 | 24 | /* Table header p w/0 margin */ 25 | .index-links a table thead th { 26 | vertical-align: middle; 27 | } 28 | 29 | table thead th p { 30 | margin: 0; 31 | } 32 | 33 | .table-nowrap td { 34 | white-space: normal !important; 35 | } 36 | 37 | /* Breathe output changes */ 38 | .breathe-sectiondef.container { 39 | background: #f9f9f9; 40 | padding: 10px; 41 | margin-bottom: 10px; 42 | border: 1px solid #efefef; 43 | } 44 | .breathe-sectiondef.container .breathe-sectiondef-title { 45 | background: #2980b9; 46 | color: #FFFFFF; 47 | padding: 4px; 48 | margin: -10px -10px 0 -10px; 49 | } 50 | .breathe-sectiondef.container .function, 51 | .breathe-sectiondef.container .member, 52 | .breathe-sectiondef.container .class, 53 | .breathe-sectiondef.container .type { 54 | border-bottom: 1px solid #efefef; 55 | } 56 | .breathe-sectiondef.container .function:last-child, 57 | .breathe-sectiondef.container .member:last-child, 58 | .breathe-sectiondef.container .class:last-child, 59 | .breathe-sectiondef.container .type:last-child { 60 | border-bottom: none; 61 | margin-bottom: 0; 62 | } 63 | 64 | /*# sourceMappingURL=common.css.map */ 65 | -------------------------------------------------------------------------------- /docs/static/css/custom.css: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/MaJerle/lwrb/3a50c7184cb809e4c06c6f9ab1b220331fe10d00/docs/static/css/custom.css -------------------------------------------------------------------------------- /docs/static/dark-light/checked.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /docs/static/dark-light/common-dark-light.css: -------------------------------------------------------------------------------- 1 | /** 2 | * @license 3 | * Copyright 2019 Google LLC 4 | * 5 | * Licensed under the Apache License, Version 2.0 (the "License"); 6 | * you may not use this file except in compliance with the License. 7 | * You may obtain a copy of the License at 8 | * 9 | * https://www.apache.org/licenses/LICENSE-2.0 10 | * 11 | * Unless required by applicable law or agreed to in writing, software 12 | * distributed under the License is distributed on an "AS IS" BASIS, 13 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 14 | * See the License for the specific language governing permissions and 15 | * limitations under the License. 16 | */ 17 | :root { 18 | --heading-color: red; 19 | --duration: 0.5s; 20 | --timing: ease; 21 | } 22 | 23 | *, 24 | ::before, 25 | ::after { 26 | box-sizing: border-box; 27 | } 28 | 29 | body { 30 | margin: 0; 31 | transition: 32 | color var(--duration) var(--timing), 33 | background-color var(--duration) var(--timing); 34 | font-family: sans-serif; 35 | font-size: 12pt; 36 | background-color: var(--background-color); 37 | color: var(--text-color); 38 | display: flex; 39 | justify-content: center; 40 | } 41 | 42 | main { 43 | margin: 1rem; 44 | max-width: 30rem; 45 | position: relative; 46 | } 47 | 48 | h1 { 49 | color: var(--heading-color); 50 | text-shadow: 0.1rem 0.1rem 0.1rem var(--shadow-color); 51 | transition: text-shadow var(--duration) var(--timing); 52 | } 53 | 54 | img { 55 | max-width: 100%; 56 | height: auto; 57 | transition: filter var(--duration) var(--timing); 58 | } 59 | 60 | p { 61 | line-height: 1.5; 62 | word-wrap: break-word; 63 | overflow-wrap: break-word; 64 | hyphens: auto; 65 | } 66 | 67 | fieldset { 68 | border: solid 0.1rem; 69 | box-shadow: 0.1rem 0.1rem 0.1rem var(--shadow-color); 70 | transition: box-shadow var(--duration) var(--timing); 71 | } 72 | 73 | div { 74 | padding: 0.5rem; 75 | } 76 | 77 | aside { 78 | position: absolute; 79 | right: 0; 80 | padding: 0.5rem; 81 | } 82 | 83 | aside:nth-of-type(1) { 84 | top: 0; 85 | } 86 | 87 | aside:nth-of-type(2) { 88 | top: 3rem; 89 | } 90 | 91 | aside:nth-of-type(3) { 92 | top: 7rem; 93 | } 94 | 95 | aside:nth-of-type(4) { 96 | top: 12rem; 97 | } 98 | 99 | #content select, 100 | #content button, 101 | #content input[type="text"], 102 | #content input[type="search"] { 103 | width: 15rem; 104 | } 105 | 106 | dark-mode-toggle { 107 | --dark-mode-toggle-remember-icon-checked: url("checked.svg"); 108 | --dark-mode-toggle-remember-icon-unchecked: url("unchecked.svg"); 109 | --dark-mode-toggle-remember-font: 0.75rem "Helvetica"; 110 | --dark-mode-toggle-legend-font: bold 0.85rem "Helvetica"; 111 | --dark-mode-toggle-label-font: 0.85rem "Helvetica"; 112 | --dark-mode-toggle-color: var(--text-color); 113 | --dark-mode-toggle-background-color: none; 114 | 115 | margin-bottom: 1.5rem; 116 | } 117 | 118 | #dark-mode-toggle-1 { 119 | --dark-mode-toggle-dark-icon: url("sun.png"); 120 | --dark-mode-toggle-light-icon: url("moon.png"); 121 | } 122 | 123 | #dark-mode-toggle-2 { 124 | --dark-mode-toggle-dark-icon: url("sun.svg"); 125 | --dark-mode-toggle-light-icon: url("moon.svg"); 126 | --dark-mode-toggle-icon-size: 2rem; 127 | --dark-mode-toggle-icon-filter: invert(100%); 128 | } 129 | 130 | #dark-mode-toggle-3, 131 | #dark-mode-toggle-4 { 132 | --dark-mode-toggle-dark-icon: url("moon.png"); 133 | --dark-mode-toggle-light-icon: url("sun.png"); 134 | } 135 | 136 | #dark-mode-toggle-3 { 137 | --dark-mode-toggle-remember-filter: invert(100%); 138 | } 139 | 140 | #dark-mode-toggle-4 { 141 | --dark-mode-toggle-active-mode-background-color: var(--accent-color); 142 | --dark-mode-toggle-remember-filter: invert(100%); 143 | } 144 | -------------------------------------------------------------------------------- /docs/static/dark-light/dark-mode-toggle.mjs: -------------------------------------------------------------------------------- 1 | /** 2 | * Copyright 2019 Google LLC 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * https://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | // @license © 2019 Google LLC. Licensed under the Apache License, Version 2.0. 18 | const doc = document; 19 | const store = localStorage; 20 | const PREFERS_COLOR_SCHEME = 'prefers-color-scheme'; 21 | const MEDIA = 'media'; 22 | const LIGHT = 'light'; 23 | const DARK = 'dark'; 24 | const MQ_DARK = `(${PREFERS_COLOR_SCHEME}:${DARK})`; 25 | const MQ_LIGHT = `(${PREFERS_COLOR_SCHEME}:${LIGHT})`; 26 | const LINK_REL_STYLESHEET = 'link[rel=stylesheet]'; 27 | const REMEMBER = 'remember'; 28 | const LEGEND = 'legend'; 29 | const TOGGLE = 'toggle'; 30 | const SWITCH = 'switch'; 31 | const APPEARANCE = 'appearance'; 32 | const PERMANENT = 'permanent'; 33 | const MODE = 'mode'; 34 | const COLOR_SCHEME_CHANGE = 'colorschemechange'; 35 | const PERMANENT_COLOR_SCHEME = 'permanentcolorscheme'; 36 | const ALL = 'all'; 37 | const NOT_ALL = 'not all'; 38 | const NAME = 'dark-mode-toggle'; 39 | const DEFAULT_URL = 'https://googlechromelabs.github.io/dark-mode-toggle/demo/'; 40 | 41 | // See https://html.spec.whatwg.org/multipage/common-dom-interfaces.html ↵ 42 | // #reflecting-content-attributes-in-idl-attributes. 43 | const installStringReflection = (obj, attrName, propName = attrName) => { 44 | Object.defineProperty(obj, propName, { 45 | enumerable: true, 46 | get() { 47 | const value = this.getAttribute(attrName); 48 | return value === null ? '' : value; 49 | }, 50 | set(v) { 51 | this.setAttribute(attrName, v); 52 | }, 53 | }); 54 | }; 55 | 56 | const installBoolReflection = (obj, attrName, propName = attrName) => { 57 | Object.defineProperty(obj, propName, { 58 | enumerable: true, 59 | get() { 60 | return this.hasAttribute(attrName); 61 | }, 62 | set(v) { 63 | if (v) { 64 | this.setAttribute(attrName, ''); 65 | } else { 66 | this.removeAttribute(attrName); 67 | } 68 | }, 69 | }); 70 | }; 71 | 72 | const template = doc.createElement('template'); 73 | // ⚠️ Note: this is a minified version of `src/template-contents.tpl`. 74 | // Compress the CSS with https://cssminifier.com/, then paste it here. 75 | // eslint-disable-next-line max-len 76 | template.innerHTML = `
`; 77 | 78 | export class DarkModeToggle extends HTMLElement { 79 | static get observedAttributes() { 80 | return [MODE, APPEARANCE, PERMANENT, LEGEND, LIGHT, DARK, REMEMBER]; 81 | } 82 | 83 | constructor() { 84 | super(); 85 | 86 | installStringReflection(this, MODE); 87 | installStringReflection(this, APPEARANCE); 88 | installStringReflection(this, LEGEND); 89 | installStringReflection(this, LIGHT); 90 | installStringReflection(this, DARK); 91 | installStringReflection(this, REMEMBER); 92 | 93 | installBoolReflection(this, PERMANENT); 94 | 95 | this._darkCSS = null; 96 | this._lightCSS = null; 97 | 98 | doc.addEventListener(COLOR_SCHEME_CHANGE, (event) => { 99 | this.mode = event.detail.colorScheme; 100 | this._updateRadios(); 101 | this._updateCheckbox(); 102 | }); 103 | 104 | doc.addEventListener(PERMANENT_COLOR_SCHEME, (event) => { 105 | this.permanent = event.detail.permanent; 106 | this._permanentCheckbox.checked = this.permanent; 107 | }); 108 | 109 | this._initializeDOM(); 110 | } 111 | 112 | _initializeDOM() { 113 | const shadowRoot = this.attachShadow({mode: 'open'}); 114 | shadowRoot.appendChild(template.content.cloneNode(true)); 115 | 116 | // We need to support `media="(prefers-color-scheme: dark)"` (with space) 117 | // and `media="(prefers-color-scheme:dark)"` (without space) 118 | this._darkCSS = doc.querySelectorAll(`${LINK_REL_STYLESHEET}[${MEDIA}*=${PREFERS_COLOR_SCHEME}][${MEDIA}*="${DARK}"]`); 119 | this._lightCSS = doc.querySelectorAll(`${LINK_REL_STYLESHEET}[${MEDIA}*=${PREFERS_COLOR_SCHEME}][${MEDIA}*="${LIGHT}"]`); 120 | 121 | // Get DOM references. 122 | this._lightRadio = shadowRoot.querySelector('[part=lightRadio]'); 123 | this._lightLabel = shadowRoot.querySelector('[part=lightLabel]'); 124 | this._darkRadio = shadowRoot.querySelector('[part=darkRadio]'); 125 | this._darkLabel = shadowRoot.querySelector('[part=darkLabel]'); 126 | this._darkCheckbox = shadowRoot.querySelector('[part=toggleCheckbox]'); 127 | this._checkboxLabel = shadowRoot.querySelector('[part=toggleLabel]'); 128 | this._legendLabel = shadowRoot.querySelector('legend'); 129 | this._permanentAside = shadowRoot.querySelector('aside'); 130 | this._permanentCheckbox = 131 | shadowRoot.querySelector('[part=permanentCheckbox]'); 132 | this._permanentLabel = shadowRoot.querySelector('[part=permanentLabel]'); 133 | 134 | // Does the browser support native `prefers-color-scheme`? 135 | const hasNativePrefersColorScheme = 136 | matchMedia(MQ_DARK).media !== NOT_ALL; 137 | // Listen to `prefers-color-scheme` changes. 138 | if (hasNativePrefersColorScheme) { 139 | matchMedia(MQ_DARK).addListener(({matches}) => { 140 | this.mode = matches ? DARK : LIGHT; 141 | this._dispatchEvent(COLOR_SCHEME_CHANGE, {colorScheme: this.mode}); 142 | }); 143 | } 144 | // Set initial state, giving preference to a remembered value, then the 145 | // native value (if supported), and eventually defaulting to a light 146 | // experience. 147 | const rememberedValue = store.getItem(NAME); 148 | if (rememberedValue && [DARK, LIGHT].includes(rememberedValue)) { 149 | this.mode = rememberedValue; 150 | this._permanentCheckbox.checked = true; 151 | this.permanent = true; 152 | } else if (hasNativePrefersColorScheme) { 153 | this.mode = matchMedia(MQ_LIGHT).matches ? LIGHT : DARK; 154 | } 155 | if (!this.mode) { 156 | this.mode = LIGHT; 157 | } 158 | if (this.permanent && !rememberedValue) { 159 | store.setItem(NAME, this.mode); 160 | } 161 | 162 | // Default to toggle appearance. 163 | if (!this.appearance) { 164 | this.appearance = TOGGLE; 165 | } 166 | 167 | // Update the appearance to either of toggle or switch. 168 | this._updateAppearance(); 169 | 170 | // Update the radios 171 | this._updateRadios(); 172 | 173 | // Make the checkbox reflect the state of the radios 174 | this._updateCheckbox(); 175 | 176 | // Synchronize the behavior of the radio and the checkbox. 177 | [this._lightRadio, this._darkRadio].forEach((input) => { 178 | input.addEventListener('change', () => { 179 | this.mode = this._lightRadio.checked ? LIGHT : DARK; 180 | this._updateCheckbox(); 181 | this._dispatchEvent(COLOR_SCHEME_CHANGE, {colorScheme: this.mode}); 182 | }); 183 | }); 184 | this._darkCheckbox.addEventListener('change', () => { 185 | this.mode = this._darkCheckbox.checked ? DARK : LIGHT; 186 | this._updateRadios(); 187 | this._dispatchEvent(COLOR_SCHEME_CHANGE, {colorScheme: this.mode}); 188 | }); 189 | 190 | // Make remembering the last mode optional 191 | this._permanentCheckbox.addEventListener('change', () => { 192 | this.permanent = this._permanentCheckbox.checked; 193 | this._dispatchEvent(PERMANENT_COLOR_SCHEME, { 194 | permanent: this.permanent, 195 | }); 196 | }); 197 | 198 | // Finally update the mode and let the world know what's going on 199 | this._updateMode(); 200 | this._dispatchEvent(COLOR_SCHEME_CHANGE, {colorScheme: this.mode}); 201 | this._dispatchEvent(PERMANENT_COLOR_SCHEME, { 202 | permanent: this.permanent, 203 | }); 204 | } 205 | 206 | attributeChangedCallback(name, oldValue, newValue) { 207 | if (name === MODE) { 208 | if (![LIGHT, DARK].includes(newValue)) { 209 | throw new RangeError(`Allowed values: "${LIGHT}" and "${DARK}".`); 210 | } 211 | // Only show the dialog programmatically on devices not capable of hover 212 | // and only if there is a label 213 | if (matchMedia('(hover:none)').matches && this.remember) { 214 | this._showPermanentAside(); 215 | } 216 | if (this.permanent) { 217 | store.setItem(NAME, this.mode); 218 | } 219 | this._updateRadios(); 220 | this._updateCheckbox(); 221 | this._updateMode(); 222 | } else if (name === APPEARANCE) { 223 | if (![TOGGLE, SWITCH].includes(newValue)) { 224 | throw new RangeError(`Allowed values: "${TOGGLE}" and "${SWITCH}".`); 225 | } 226 | this._updateAppearance(); 227 | } else if (name === PERMANENT) { 228 | if (this.permanent) { 229 | store.setItem(NAME, this.mode); 230 | } else { 231 | store.removeItem(NAME); 232 | } 233 | this._permanentCheckbox.checked = this.permanent; 234 | } else if (name === LEGEND) { 235 | this._legendLabel.textContent = newValue; 236 | } else if (name === REMEMBER) { 237 | this._permanentLabel.textContent = newValue; 238 | } else if (name === LIGHT) { 239 | this._lightLabel.textContent = newValue; 240 | if (this.mode === LIGHT) { 241 | this._checkboxLabel.textContent = newValue; 242 | } 243 | } else if (name === DARK) { 244 | this._darkLabel.textContent = newValue; 245 | if (this.mode === DARK) { 246 | this._checkboxLabel.textContent = newValue; 247 | } 248 | } 249 | } 250 | 251 | _dispatchEvent(type, value) { 252 | this.dispatchEvent(new CustomEvent(type, { 253 | bubbles: true, 254 | composed: true, 255 | detail: value, 256 | })); 257 | } 258 | 259 | _updateAppearance() { 260 | // Hide or show the light-related affordances dependent on the appearance, 261 | // which can be "switch" or "toggle". 262 | const appearAsToggle = this.appearance === TOGGLE; 263 | this._lightRadio.hidden = appearAsToggle; 264 | this._lightLabel.hidden = appearAsToggle; 265 | this._darkRadio.hidden = appearAsToggle; 266 | this._darkLabel.hidden = appearAsToggle; 267 | this._darkCheckbox.hidden = !appearAsToggle; 268 | this._checkboxLabel.hidden = !appearAsToggle; 269 | } 270 | 271 | _updateRadios() { 272 | if (this.mode === LIGHT) { 273 | this._lightRadio.checked = true; 274 | } else { 275 | this._darkRadio.checked = true; 276 | } 277 | } 278 | 279 | _updateCheckbox() { 280 | if (this.mode === LIGHT) { 281 | this._checkboxLabel.style.setProperty(`--${NAME}-checkbox-icon`, 282 | `var(--${NAME}-light-icon,url("${DEFAULT_URL}moon.png"))`); 283 | this._checkboxLabel.textContent = this.light; 284 | if (!this.light) { 285 | this._checkboxLabel.ariaLabel = DARK; 286 | } 287 | this._darkCheckbox.checked = false; 288 | } else { 289 | this._checkboxLabel.style.setProperty(`--${NAME}-checkbox-icon`, 290 | `var(--${NAME}-dark-icon,url("${DEFAULT_URL}sun.png"))`); 291 | this._checkboxLabel.textContent = this.dark; 292 | if (!this.dark) { 293 | this._checkboxLabel.ariaLabel = LIGHT; 294 | } 295 | this._darkCheckbox.checked = true; 296 | } 297 | } 298 | 299 | _updateMode() { 300 | if (this.mode === LIGHT) { 301 | this._lightCSS.forEach((link) => { 302 | link.media = ALL; 303 | link.disabled = false; 304 | }); 305 | this._darkCSS.forEach((link) => { 306 | link.media = NOT_ALL; 307 | link.disabled = true; 308 | }); 309 | } else { 310 | this._darkCSS.forEach((link) => { 311 | link.media = ALL; 312 | link.disabled = false; 313 | }); 314 | this._lightCSS.forEach((link) => { 315 | link.media = NOT_ALL; 316 | link.disabled = true; 317 | }); 318 | } 319 | } 320 | 321 | _showPermanentAside() { 322 | this._permanentAside.style.visibility = 'visible'; 323 | setTimeout(() => { 324 | this._permanentAside.style.visibility = 'hidden'; 325 | }, 3000); 326 | } 327 | } 328 | 329 | customElements.define(NAME, DarkModeToggle); -------------------------------------------------------------------------------- /docs/static/dark-light/dark.css: -------------------------------------------------------------------------------- 1 | /** 2 | * @license 3 | * Copyright 2019 Google LLC 4 | * 5 | * Licensed under the Apache License, Version 2.0 (the "License"); 6 | * you may not use this file except in compliance with the License. 7 | * You may obtain a copy of the License at 8 | * 9 | * https://www.apache.org/licenses/LICENSE-2.0 10 | * 11 | * Unless required by applicable law or agreed to in writing, software 12 | * distributed under the License is distributed on an "AS IS" BASIS, 13 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 14 | * See the License for the specific language governing permissions and 15 | * limitations under the License. 16 | */ 17 | :root { 18 | color-scheme: dark; /* stylelint-disable-line property-no-unknown */ 19 | 20 | --background-color: rgb(15 15 15); 21 | --text-color: rgb(240 240 240); 22 | --shadow-color: rgb(240 240 240 / 50%); 23 | --accent-color: rgb(0 0 240 / 50%); 24 | } 25 | 26 | img { 27 | filter: grayscale(50%); 28 | } 29 | 30 | .icon { 31 | filter: invert(100%); 32 | } 33 | 34 | a { 35 | color: yellow; 36 | } 37 | -------------------------------------------------------------------------------- /docs/static/dark-light/light.css: -------------------------------------------------------------------------------- 1 | /** 2 | * @license 3 | * Copyright 2019 Google LLC 4 | * 5 | * Licensed under the Apache License, Version 2.0 (the "License"); 6 | * you may not use this file except in compliance with the License. 7 | * You may obtain a copy of the License at 8 | * 9 | * https://www.apache.org/licenses/LICENSE-2.0 10 | * 11 | * Unless required by applicable law or agreed to in writing, software 12 | * distributed under the License is distributed on an "AS IS" BASIS, 13 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 14 | * See the License for the specific language governing permissions and 15 | * limitations under the License. 16 | */ 17 | :root { 18 | color-scheme: light; /* stylelint-disable-line property-no-unknown */ 19 | 20 | --background-color: rgb(240 240 240); 21 | --text-color: rgb(15 15 15); 22 | --shadow-color: rgb(15 15 15 / 50%); 23 | --accent-color: rgb(240 0 0 / 50%); 24 | } 25 | -------------------------------------------------------------------------------- /docs/static/dark-light/moon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/MaJerle/lwrb/3a50c7184cb809e4c06c6f9ab1b220331fe10d00/docs/static/dark-light/moon.png -------------------------------------------------------------------------------- /docs/static/dark-light/moon.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | moon 5 | 6 | 7 | 8 | -------------------------------------------------------------------------------- /docs/static/dark-light/sun.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/MaJerle/lwrb/3a50c7184cb809e4c06c6f9ab1b220331fe10d00/docs/static/dark-light/sun.png -------------------------------------------------------------------------------- /docs/static/dark-light/sun.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | -------------------------------------------------------------------------------- /docs/static/dark-light/unchecked.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /docs/static/images/buff_cases.xml: -------------------------------------------------------------------------------- 1 | 7ZxNc6JAEIZ/DcdUCcMMeIwas3vYy6ZqU7uXLQKjUEHHRYxmf/3yMSjSWpWtBHrI4MHCHuSjH3q631YwyHR1uE+8TfhNBDw2rFFwMMjMsKwxM7P33PBaGhiVhmUSBaWpZniI/nJpHEnrLgr49mzFVIg4jTbnRl+s19xPz2xekoj9+WoLEZ/vdeMtOTA8+F4MrY9RkIbSarLxaeALj5ah3LVrOeXAk+c/LxOxW8v9GRZZFK9yeOVV25Inug29QOxrJnJnkGkiRFourQ5THueurdxWfm9+ZfR43Alfp2/5wtfJ7JfnRL8PP/f3u5C8hOmP+Y1Dys28ePFOOuS7PNz0tXJRcZI838zIIJN9GKX8YeP5+eg+uyYyW5iu4uyTmS0uxDqVlM189UUUx1MRi6TYFvF9TjMXkck2TcQzr43MzfF0PD6OVDCO25THc+G8pemFJyk/1EzSD/dcrHiavGaryFHblkzkNWu68vP+dAVU2MIa+8rmyWtuedzyye/ZgnT9/2CwAYZHw2Jxtt/JU5ItLfOlVsEsFr5fcz8OGGo1wNjIYGwIRoP4MBvxgR0eNh3Co3CwpRYXqmX2sBSLDjokj2KUNJMHQwbDtAwPolh4sCE8yqK3ER5Hj2OBqS4MvcKDqqY9XBNg0DI+mGrpA2YPs86lBoP92eVdhMIlN9vCz7fZCuZocyjQVePtwIRormNsgJ8WrzYUDGvAHEGYrFOYLqBpaYjhQs7pFsMYYCAaYrjQVukUgw1TP9MPg4U9Kdkw9VP9MFyqwLrFYAEMtoYYLtRb3WKABZejH4ZLsrBTDHBOGr2fwtxbRXF+hrdJ5MU9RYM+TzGARk1F0gAUUO4GNnbXWDVFYjuA5gcrkl5gwJ7ubCgMP1iR9AIDuiKBwlADRdLEgK5IKBSGGigSEA3YmZ7CIkwDRQIwYCsSCoWhBooETErYKZrCv590IUn6kLfRJ6pBkrzjl3rVJAnVUpIADOjznZaSBGDAliS0dUnSBwzokqTaWXuSpA8Y0CUJa12S9AIDtiRhQy18jQ16hAy18Dv+HalaLcy0rIUBBuxamLVeC6vYggEYsGthpmUt3MSAXgs7WtbCIBqwM72jZXseYMCuhR0LYPjg9nwfogG9Pe8MkuQaG+yJirUeISpOVKCFhR4hUBlqcA/I8eqvMFCIodunPEBJd/v5MZjKYYCSbvr5MRDlMEBJN/v8GGzVMLgwRQ9dwzffzKta19CF9zBo0DUEGLALLhc+c0CDX9ABBuyuoQuVoQZdwyYG9K6hC+WHBl1DEA3YYtyF8kODriHAgN01dKH80KBrCCYl9BQN5YeuXcMmG+yJagx/ZbrTYKLqThlmH0/PnS3Gas/2JXf/AA== -------------------------------------------------------------------------------- /docs/static/images/buff_lin_read_skip.xml: -------------------------------------------------------------------------------- 1 | 7VvLbqMwFP0alpWwDZQsG/qSRpGidtHHZkTBBFQnzhBnQubrh4edkNxU09EUTN1hEZlrx8bnXIzPSbBIMC9u8nCZTnhMmYXtuLDIpYUxcf3yswpsm4Dr2U1glmdxE0L7wH32i8qgarbOYro6aCg4ZyJbHgYjvljQSBzEwjznm8NmCWeHoy7DGQWB+yhkMPqQxSKVUeSN9hW3NJulcmgfnzcVL2H0Osv5eiHHszBJ6qOpnoeqLznRVRrGfNMKkSuLBDnnoinNi4CyCloFW/O96zdqd9ed04V4zxe28dofrZ+L6ZOYTsbf2eOteD6TvfwM2VricSevVmwVQvUcadWLbZHxJs0EvV+GUVW7KVOijKVizsozVBYTvhCSZFQ1TzLGAs54XvdFooi6JUJkvBI5f6Wtmms0CkYj1YMc/cQk1RXTXNCiFZKTvqF8TkW+LZuoWpWQMkGRL883e7oVR2mLaBULZYLNdj3vQS4LEue/wBwDzB8s7LFy2PFLXpZmValTFpIkimqs+2MBO0cseJpZIIAF1GahBb33Y13dozUkZ6sa1YuyAbKXRU2Uqu+Futilfuz0ewP5R9TZkDqvT+ocQB02HnOsGXMXYE6Mx9zRjLkHMPdMxxzrXlvOAeau6Zif2hD1irkPMHc+FvP3YgvYCepj90z/WNhP7IB6hX0EYD83PdWx7seoUr0t0O1/B/06nGesmuJFnoXsUzChe81BUPledJr9u6WkR81lH2W/q1lzISh9jbMbsPfHRO8Xcyh0v4DdQIZmNyAoWv/7DW/cQUPzGxAUv8YZDgB07TslqH6NcxwA6LodBwTlb7eWg45d0dAsBwT1b7eewwBA1+45IKh+TTQdAO66TQfVcW+uwwCSXbvrgKHY/RquwzETulcdtW9sEdHt9l1D9pOhuQ4YKmDjXAdy/CcH3XoXQ737BWwHZ2i2A4bS9VPYDjrWraHZDhgq4G5thyGArn2rBBVwt7bDEEDXbTtgqICNsx2OQdduO2CogI2zHUCm6xYABMpfE20HgLtu24FABWyc7QBWGN3PUgLVrgbbYQBMaF91oAIOehZjGlhwNBoR3ybu5dkVWhXTu0f+xJPpZLI69feHIW5zAMIneBjCryvl6f7tmbqu9YYSufoN -------------------------------------------------------------------------------- /docs/static/images/buff_lin_write_advance.xml: -------------------------------------------------------------------------------- 1 | 7ZvPc6IwFID/Go47QxKgemzZ2r3sZXvomUIEptG4MVbdv375kSj6cLYzuzxsXA8deEmI+V4I+QbrsXixe1LJqvguMy486mc7j331KA2YX/2tA/s2EN2RNpCrMmtDncBz+YuboGmXb8qMr08qaimFLlenwVQulzzVJ7FEKbk9rTaX4rTXVZJzEHhOEwGjL2WmizY6Cf1j/Bsv88L2THxT8pqkb7mSm6Xpz6Ns3nza4kVir2Xqr4skk9tOiD16LFZS6vZosYu5qNFabG272YXSw/dWfKk/0oC2Dd4TsTFD/2G+mN5bGM1weN3A99jDtig1f14laV26rbJfxQq9ENUZqQ7ncqlNPkldfV4KEUshVXMtlqY8rGCwh7VW8o13SmZkGk+n9gqm957xmNA7V5rvOiEzvicuF1yrfVXFlkaG9f7sfHvMrE1H0UmqjSVmLuWHKx95VgcGaT9eBvC+eDQSVQ8Pr6o6yuujQYHP52naYMUDToPxgAcAOOkC71COfm7qm6wZ/Zd1A/C+qkD81a7JiS1HyVLcfHBvi8lZlnyYpWigLIUgS9R5vBQPbwTwMufxBnh47wDeyHW8FHFxmAC8oet4D+cIeKcAb/Bv8X4U4+VEDEG4Zw8yFGErMR3Ed67PYIr4eCME8PX/nu8sWZSiHs29KhPxKaAjrhoEiuK9a3P68JCzczqEfIfyFgJN0TkRpyOKOIFieAMmzoI/LhiDAYeO99/EL9wXI5o4ga7onIoDvph7FSiLzrk44Ivo4gTa4rAynoV8kgWj8sWUcQJ1cVgbvwK+mDZurzGcjl8hX0QXp9AVh3XxK+CL6eJ2t3BzLn4OHXPNgK7o3J6YjejiPS8RnXNxNqKL97z+uwEXD87fivfskgcjDi3vU8j4CA9TNqKM97xZHFbGr4Ev5mYFyqJzMg74Isp4z7tF52T8nC+mjNtfVbos42D+Im6sGZRF52Qc8EWUcQZl0TkZB+sD4vONQTEcQcavADrmmgFlMUaWmRF2GQGenlenx5/DN2Wdfzlgj78B -------------------------------------------------------------------------------- /docs/static/images/buff_thread_safety_2_main_irq_write.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 |
Main loop
Main loop
Interrupt context
Interrupt context
RB
RB
Main loop
Main loop
-------------------------------------------------------------------------------- /docs/static/images/buff_thread_safety_2_main_irq_write.xml: -------------------------------------------------------------------------------- 1 | 7ZfbcpswEIafhst0DAJMLoMPaSbJ9OBpYl/KaG00lZFHyKc8fUWQOBg8dlrXnTa9Cvtrd0Hf/hHYQr3F9lbgZfzICTDL6ZCthfqW49idrqv+ZMouV649LxfmghKdVAoj+gKmUqsrSiCtJUrOmaTLuhjxJIFI1jQsBN/U02ac1e+6xHNoCKMIs6b6TImMczXwOqX+Eeg8Nne2O3plgU2yFtIYE76pSGhgoZ7gXOZXi20PWAbPcMnrhgdWiwcTkMhTCtDtE0mGwd14PAsfnkYvchxOrnSXNWYrvWH9sHJnCAi+SghkTToWCjcxlTBa4ihb3aiZKy2WC6YiW13OKGM9zrhQccITlRQSnMav5dl6KgX/DibDctDgxg99X63oBwEhYXtwh3bBTRkO+AKk2KkUU+Br1NprjpnFppyc7Wotrk7NJGLtlnnRuwSqLjTTdr5fBBqKKzF5uO+tP02u7/s4/NbC9xHTRCmM8+X5UCuQBEMwi9oQ+1EA01mBuMGzhfphxN09xHYLYqcFsfu7CDsNwneJBCFWS5kfC2pn8l8g7f1p0qhB+mt4XrAeBMRtAxs4U1Q5JX4JLLL3wKIm2OCSXN2LnRGzIIKo1bnTwHO9znkAuycAvqxzveNvOUjITfa5oKKI4TSlUZ2kwiF2Y039NZhkwQfPhP1tdbG/M9GWyrHpoa4rVSoqi7LA1BycQcpXIoLjrxuJxRzk8X9lILWPn+ZEKxPzWgZmNAEMS7qufzK1TVHf4TOniawcdV7dMKi7Z4R837qq+omz16h44ZtGzl6jHEyj0aupim3/vM/89+Iz56/0WXGs7x9Mb/VZ4SvTyL+sz7rvxWfoRJ+5/332Np+psPzlmaeXv9/R4Ac= -------------------------------------------------------------------------------- /docs/static/images/buff_thread_safety_2_thread_read.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 |
Producer thread
Producer thread
RB
RB
Thread 1
Thread 1
Thread 2
Thread 2
-------------------------------------------------------------------------------- /docs/static/images/buff_thread_safety_2_thread_read.xml: -------------------------------------------------------------------------------- 1 | 7VdNc9owEP01HNOxLduYY/govXSSlkwKR2GtsSfCYmQBJr++Mpb8zQAZmqTTnqx90q6tt2+1Vg+N1umU4034nRGgPcsgaQ+Ne5ZlGn1bPjLkkCMDx8mBFY+IWlQCs+gVtKdCtxGBpLZQMEZFtKmDPotj8EUNw5yzfX1ZwGj9rRu8ghYw8zFto78iIsIc9RyjxL9BtAr1m01DzayxXqyAJMSE7SsQmvTQiDMm8tE6HQHNyNO85H5fT8wWH8YhFpc49F+f2cN44IyG3jy0Ar576s/vVJQdplu14UfOyNYHnhEdcsBEfb04aEo428YEsqhGDw33YSRgtsF+NruXIpBYKNZUWqYcBhGlI0YZP/oigsELfIkngrMXqMy4vgfLQM6096U/EriAtAKpfU6BrUHwg1yiZ13FuRJdkYN9mUJTY2ElfbbCsFLNqghdEisHitsreLZaPP8c3pZYBzxidxHrWUvkurchFhnnifXek1fU4vVJidYwb8pv4Pngdwp36Tm2Y9yGX7vJr/HRwrVbBLdohZjcZyettHyKkyTy60xKOvhhrlg/GovM+OJoc5xWJ8cHbaWRmOsYclzxklbplBna52QOErblPpw/CQXmKxDnKxlIrW+0M1rJmNORMI1xoFhEu3q36cqiesMji+TOypPOrgsGuQ0h5PtWXtXu0Aw0aAQyG4FyYlqBjqIqtv12nTn/is6sC3WGPpXOrP6Jg/9anRW60oGc99WZe7phtBX3FzYM+6MbRv9/IXcq7pMUMhqcqb9LC9luFjL6Y4X89LyY8rsf/cnLw1hgMV2M57Tj5nK78o1ZLBcNCU7Co7vZVcaTe3dY+a9u1WyHqk7/VzeyUnTjahnbHaoorptX1LE0y1tnno7y7o4mvwE= -------------------------------------------------------------------------------- /docs/static/images/buff_thread_safety_2_thread_read_write.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 |
Producer thread
Producer thread
RB
RB
Thread 1
Thread 1
Thread 2
Thread 2
Producer thread
Producer thread
-------------------------------------------------------------------------------- /docs/static/images/buff_thread_safety_2_thread_read_write.xml: -------------------------------------------------------------------------------- 1 | 7ZhLc9owEMc/jY/p2JJt7GN4JL10kjZMCqeOsARWYyxGFq98+sqxhF9ioIRAZtoT3pV2bf32rxcW7M039xwt4m8Mk8QCNt5YsG8B4IDAlj+5Z1t4OiAsHDNOsepUOp7oK1FOFTdbUkyyWkfBWCLoou6MWJqSSNR8iHO2rnebsqT+1gWakZbjKUJJ2/uTYhEX3sCzS/9XQmexfrNjq5Y50p2VI4sRZuuKCw4s2OOMieJpvumRJIenuRRxd3tadx/GSSqOCXicrMLwd9ab+7/omID18PVueAOKLCuULNWA1ceKrSbA2TLFJE9iW7C7jqkgTwsU5a1rWXPpi8U8kZYjH6c0SXosYVzaKUtlpy5GWfwWnrdngrMXontYAA5u/a7vyxb1IYQLstk7QmfHTQqOsDkRfCu76ABdFaU1J1T2uqyc4ypfXK2aLhpSapntcpdA5YNiaubbeX1mD/3Q63WDUQymfDXsjG6cFt9HzvAyIjwXcswJwucDLnFiRIJpZALtRwGZTHegW1QN7PeD9uuggW0ADQyg3Y/i3Nbxj+55wXokwK4JbAAmsKLgd4GFdgMsaIMNLskVtrgOlWht56x8p0FEIqNwJ4HnevZ5+LpNvlcXrnt4ASYpvs13MmlFCcoyGtVJShx8O1LU34xxbnzxtNnfVBv7W21tqBjpHPK5EiWtMig3dMzeGmRsySNyeCUUiM+IODyTCa7ty+2KVirmGQqmfZwkSNBVfTc3VVG94ZFRObJypXPrgoF+QwjFuFVUdfdtJgobiZxGogJMK9GbqHbDPl1n3r+iM3CkzuCn0hno7Fn4/1ZnO13pRN5ldebv3zDOeLS83obhXnvD6PyfyEbFfZKJDMMD8+/Yiew2JzL8sIk8fB7f85vvncHLQ18gcT/ujxLDzeV80/eUm+H7ztWNqlz2Zmi8eX/am+H7ruDNm+El10sj5ysefIwLk1HJx52Kr3Y4CRpFPfUQDOyPOgRLs/wnrehe/h8JB38A -------------------------------------------------------------------------------- /docs/static/images/buff_thread_safety_2_thread_write.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 |
Thread 1
Thread 1
Thread 2
Thread 2
RB
RB
Thread 3
Thread 3
-------------------------------------------------------------------------------- /docs/static/images/buff_thread_safety_2_thread_write.xml: -------------------------------------------------------------------------------- 1 | 7Vffk5owEP5reLQDBJB7PNR6czN2nNr29N4iWYU7JE6Iiv3rGyThh+CN9hw77fXJ7LfZDfn2k1001FulQ4bXwYgSiDRTJ6mG+pppGnrXEj8Zss+RO9vOgSULidxUApPwJ6hIiW5CAkltI6c04uG6Dvo0jsHnNQwzRnf1bQsa1U9d4yU0gImPoyb6FBIe5Khr6yX+AOEyUCcbuvSssNosgSTAhO4qEBpoqMco5flqlfYgyshTvORxn094iwdjEPNzAmLr0Ru/dpLEY3j7Mn3EPzp+JwvI0mxxtJE3/hYwwERg8oSE7xUZjG5iAlk+XUPeLgg5TNbYz7w7UX6BBXwVCcsQy0UYRT0aUXaIRQSDu/AFnnBGX6HicXwX5gvhad5IPsIWGIe0AskbDoGugLO92KK8jmRbys1U5diVxTNURYJK4SyJYamXZZG6pFQsJKuXMIxOM2z+Cwxbf5xhq8HwV++6zNrgEquNWdecI8e5DrNIP2LWbDLr3pRY+7R00VUJXrg++K3Snbu2ZevXIdg6g+AbS9dpMNzgFWJynzUyYfkRTpLQr1Mp+GD7qaT9YMwy45OtzH5adfb3ykpDPlU5xLoSJawyKDNUzMkiJHTDfDij0XDMlsDP+DcDqTXmZlErRbNbaqYwBhHm4bbeztsKKU8Y01DcrXzdWXXNIOdIC/nNZVS1/R4nujtKZBwlyplpJDroqrj2O6TW/TBSQ3+n1MzuidfTpVIrpKUS2TeWmvthpGadKzX7v9TeKbXn2Swdjr48jJ8mw+fBy2T0nfOOmgHeUNpvzyUxjcUmj+AkOIQbbfPJ4N7xKgNgYxhp0dXp0dquk1m0jOp8YrWoovjovGBAEWb57ZmXo/yCR4Nf -------------------------------------------------------------------------------- /docs/static/images/buff_thread_safety_single_read_write.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 |
Single write
entry point
[Not supported by viewer]
RB
RB
Single read
exit point
Single read<br>exit point
-------------------------------------------------------------------------------- /docs/static/images/buff_thread_safety_single_read_write.xml: -------------------------------------------------------------------------------- 1 | 5VZNj9owEP01OVLle7PHArutVqqEitRuj8YeEnedGDkGQn99bWzng4CWVen20FM8z57x+L0XJ140K5tPAm2KL5wA80KfNF4098IwCDNfPTRyMMhdeG+AXFBiF3XAkv4CC9q8fEsJ1IOFknMm6WYIYl5VgOUAQ0Lw/XDZmrPhrhuUwwhYYsTG6HdKZGHQLPE7/DPQvHA7B76dKZFbbIG6QITve1D04EUzwbk0o7KZAdPkOV5M3uOF2bYxAZW8JqGKn6aLl0ldTwXa/Xx+Qt8meKITdJkdYlt74iWtcnX6MEXlxoum1arWj72gUoNM7TVdCTXK9UjtLQ66DU5VF+ac8uDIE3xbEdD7+7pEoUosNwjr2b2yi8IKWTIVBWq4pozNOOPimBsRBNkaK7yWgr9AbybFGazWasa2DkJCc5GUoKVaeRR4CaZhl5Badaw9W7X2ndiBw4qe0LHFkPVX3pbuJFADq8JbFIlHinyd3pbZBDISn2M2C1dRmt6G2ch/ndnsXYlNLlrdF4DIGXM3VP4Vb68zDPist1dZEif+bRSIr1Dgnb2djiQY8QoV+ajvbRVhhuqa4iGVxxvn2dJ+DH7o4EPiwnnTn5wfXKTEfHY11LiXpaIuSQcuxzQHZPSJOJFAHYBvBYYrLlqJRA7y1fd/LGpPtOSMZg4TwJCku2HD54S0OyyMudv7MB56JkpPvGAOumhficdLhe5PCgUnhQwRo0JHX7XH/gOrZf+v1eJrrZb8S6uFdxeup7darbWWK5Tcymoq7P7PzPLuLzd6+A0= -------------------------------------------------------------------------------- /docs/static/images/logo.drawio: -------------------------------------------------------------------------------- 1 | jZJNb4MwDIZ/DcdJQNSKXcu6Tto6TTCp54i4JFogKLgD9usXhgNFVaWdEj/+iPPaAUur/mB5I49GgA7iUPQBewriOAqTxB0jGSbyuCVQWiUoaAG5+oEJbghelIB2FYfGaFTNGhamrqHAFePWmm4ddjZ6/WjDS7gBecH1LT0pgXKiySZc+AuoUvqXo5A8FffBBFrJhemuqrJ9wFJrDE63qk9Bj9p5Waa85zveuTELNf4noeVMvOeDyrg6vn6cs1NSfj7EU5Vvri/04bcu21HDOHgVrLnUAsZCUcB2nVQIecOL0du5sTsmsdLkbtGar1mtrSNnUyNNlo32LEXoDOoALEJ/92uLYG7RwFSAdnAhlMD8OGjHYm93y8iiLTF5NS4fx2lLyrn0IqS7kJbeXGb257tafLb/BQ== -------------------------------------------------------------------------------- /docs/static/images/logo.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 |
LwRB
LwRB
-------------------------------------------------------------------------------- /docs/static/images/logo_tm.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/MaJerle/lwrb/3a50c7184cb809e4c06c6f9ab1b220331fe10d00/docs/static/images/logo_tm.png -------------------------------------------------------------------------------- /docs/static/images/logo_tm_full.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/MaJerle/lwrb/3a50c7184cb809e4c06c6f9ab1b220331fe10d00/docs/static/images/logo_tm_full.png -------------------------------------------------------------------------------- /docs/tips-tricks/index.rst: -------------------------------------------------------------------------------- 1 | .. _tips_tricks: 2 | 3 | Tips & tricks 4 | ============= 5 | 6 | Application buffer size 7 | ^^^^^^^^^^^^^^^^^^^^^^^ 8 | 9 | Buffer size shall always be ``1`` byte bigger than anticipated data size. 10 | 11 | When application uses buffer for some data block ``N`` times, it is advised to set buffer size to ``1`` byte more than ``N * block_size`` is. 12 | This is due to ``R`` and ``W`` pointers alignment. 13 | 14 | .. note:: 15 | For more information, check :ref:`how_it_works`. 16 | 17 | .. literalinclude:: ../examples_src/example_tt_buff_size.c 18 | :language: c 19 | :linenos: 20 | :caption: Application buffer size assignment 21 | 22 | When the code is executed, it produces following output: 23 | 24 | .. literalinclude:: ../examples_src/example_tt_buff_size_log.c 25 | :caption: Application buffer size assignment output 26 | 27 | .. toctree:: 28 | :maxdepth: 2 -------------------------------------------------------------------------------- /docs/user-manual/events.rst: -------------------------------------------------------------------------------- 1 | .. _events: 2 | 3 | Events 4 | ====== 5 | 6 | When using LwRB in the application, it may be useful to get notification on different events, 7 | such as info when something has been written or read to/from buffer. 8 | 9 | Library has support for events that get called each time there has been a modification 10 | in the buffer data, that means on every read or write operation. 11 | 12 | Some use cases: 13 | 14 | * Notify application layer that LwRB operation has been executed and send debug message 15 | * Unlock semaphore when sufficient amount of bytes have been written/read from/to buffer when application uses operating system 16 | * Write notification to message queue at operating system level to wakeup another task 17 | 18 | .. note:: Every operation that modified `read` or `write` internal pointers, 19 | is considered as read or write operation. An exception is *reset* event that sets 20 | both internal pointers to `0` 21 | 22 | .. literalinclude:: ../examples_src/example_events.c 23 | :language: c 24 | :linenos: 25 | :caption: Example code for events 26 | 27 | .. toctree:: 28 | :maxdepth: 2 -------------------------------------------------------------------------------- /docs/user-manual/how-it-works.rst: -------------------------------------------------------------------------------- 1 | .. _how_it_works: 2 | 3 | How it works 4 | ============ 5 | 6 | This section shows different buffer corner cases and provides basic understanding how data are managed internally. 7 | 8 | .. figure:: ../static/images/buff_cases.svg 9 | :align: center 10 | :alt: Different buffer corner cases 11 | 12 | Different buffer corner cases 13 | 14 | Let's start with reference of abbreviations in picture: 15 | 16 | * ``R`` represents `Read` pointer. Read on read/write operations. Modified on read operation only 17 | * ``W`` represents `Write` pointer. Read on read/write operations. Modified on write operation only 18 | * ``S`` represents `Size` of buffer. Used on all operations, never modified (atomic value) 19 | 20 | * Valid number of ``W`` and ``R`` pointers are between ``0`` and ``S - 1`` 21 | 22 | * Buffer size is ``S = 8``, thus valid number range for ``W`` and ``R`` pointers is ``0 - 7``. 23 | 24 | * ``R`` and ``W`` numbers overflow at ``S``, thus valid range is always ``0, 1, 2, 3, ..., S - 2, S - 1, 0, 1, 2, 3, ..., S - 2, S - 1, 0, ...`` 25 | * Example ``S = 4``: ``0, 1, 2, 3, 0, 1, 2, 3, 0, 1, ...`` 26 | 27 | * Maximal number of bytes buffer can hold is always ``S - 1``, thus example buffer can hold up to ``7`` bytes 28 | * ``R`` and ``W`` pointers always point to the next read/write operation 29 | * When ``W == R``, buffer is considered empty. 30 | * When ``W == R - 1``, buffer is considered full. 31 | 32 | * ``W == R - 1`` is valid only if ``W`` and ``R`` overflow at buffer size ``S``. 33 | * Always add ``S`` to calculated number and then use modulus ``S`` to get final value 34 | 35 | .. note:: 36 | 37 | Example 1, add ``2`` numbers: ``2 + 3 = (3 + 2 + S) % S = (3 + 2 + 4) % 4 = (5 + 4) % 4 = 1`` 38 | 39 | Example 2, subtract ``2`` numbers: ``2 - 3 = (2 - 3 + S) % S = (2 - 3 + 4) % 4 = (-1 + 4) % 4 = 3`` 40 | 41 | 42 | .. figure:: ../static/images/buff_cases.svg 43 | :align: center 44 | :alt: Different buffer corner cases 45 | 46 | Different buffer corner cases 47 | 48 | Different image cases: 49 | 50 | * Case **A**: Buffer is empty as ``W == R = 0 == 0`` 51 | * Case **B**: Buffer holds ``W - R = 4 - 0 = 4`` bytes as ``W > R`` 52 | * Case **C**: Buffer is full as ``W == R - 1`` or ``7 == 0 - 1`` or ``7 = (0 - 1 + S) % S = (0 - 1 + 8) % 8 = (-1 + 8) % 8 = 7`` 53 | 54 | * ``R`` and ``W`` can hold ``S`` different values, from ``0`` to ``S - 1``, that is modulus of ``S`` 55 | * Buffer holds ``W - R = 7 - 0 = 7`` bytes as ``W > R`` 56 | * Case **D**: Buffer holds ``S - (R - W) = 8 - (5 - 3) = 6`` bytes as ``R > W`` 57 | * Case **E**: Buffer is full as ``W == R - 1`` (``4 = 5 - 1``) and holds ``S - (R - W) = 8 - (5 - 4) ) = 7`` bytes 58 | 59 | 60 | .. toctree:: 61 | :maxdepth: 2 -------------------------------------------------------------------------------- /docs/user-manual/hw-dma-usage.rst: -------------------------------------------------------------------------------- 1 | DMA for embedded systems 2 | ======================== 3 | 4 | One of the key features of LwRB library is that it can be seamlessly integrated with DMA controllers on embedded systems. 5 | 6 | .. note:: 7 | 8 | DMA stands for *Direct Memory Access* controller and is usually used to off-load CPU. 9 | More about DMA is available on `Wikipedia `_. 10 | 11 | DMA controllers normally use source and destination memory addresses to transfer data in-between. 12 | This features, together with LwRB, allows seamless integration and zero-copy of application data at interrupts after DMA transfer has been completed. 13 | Some manual work is necessary to be handled, but this is very minor in comparison of writing byte-by-byte to buffer at (for example) each received character. 14 | 15 | Below are ``2`` common use cases: 16 | 17 | * DMA transfers data from LwRB memory to (usually) some hardware IP 18 | * DMA transfers data from hardware IP to memory 19 | 20 | Zero-copy data from LwRB memory 21 | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ 22 | 23 | This describes how to pass LwRB output memory address as pointer to DMA (or any other processing function). 24 | After data is successfully processed, application can skip processed data and mark buffer as free for new data being written to it. 25 | 26 | .. figure:: ../static/images/buff_lin_read_skip.svg 27 | :align: center 28 | :alt: Data transfer from memory to hardware IP 29 | 30 | Data transfer from memory to hardware IP 31 | 32 | * Case **A**: Initial state, buffer is full and holds ``7`` bytes 33 | * Case **B**: State after skipping ``R`` pointer for ``3`` bytes. Buffer now holds ``4`` remaining bytes 34 | * Case **C**: Buffer is empty, no more memory available for read operation 35 | 36 | Code example: 37 | 38 | .. literalinclude:: ../examples_src/example_skip_1.c 39 | :language: c 40 | :linenos: 41 | :caption: Skip buffer data after usage 42 | 43 | Part **A** on image clearly shows that not all data bytes are linked in single contiguous block of memory. 44 | To send all bytes from lwrb, it might be necessary to repeat procedure multiple times 45 | 46 | .. literalinclude:: ../examples_src/example_skip_2.c 47 | :language: c 48 | :linenos: 49 | :caption: Skip buffer data for non-contiguous block 50 | 51 | Zero-copy data to LwRB memory 52 | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ 53 | 54 | Similar to reading data from buffer with zero-copy overhead, it is possible to write to lwrb with zero-copy overhead too. 55 | Only difference is that application now needs pointer to write memory address and length of maximal number of bytes to directly copy into buffer. 56 | After successful processing, buffer advance operation is necessary to manually increase write pointer and to increase number of bytes in buffer. 57 | 58 | .. figure:: ../static/images/buff_lin_write_advance.svg 59 | :align: center 60 | :alt: Data transfer from memory to hardware IP 61 | 62 | * Case **A**: Initial state, buffer is empty as ``R == W`` 63 | 64 | * Based on ``W`` pointer position, application could write ``4`` bytes to contiguous block of memory 65 | * Case **B**: State after advancing `W` pointer for `4` bytes. Buffer now holds `4` bytes and has ``3`` remaining available 66 | * Case **C**: Buffer is full, no more free memory available for write operation 67 | 68 | Code example: 69 | 70 | .. literalinclude:: ../examples_src/example_advance_1.c 71 | :language: c 72 | :linenos: 73 | :caption: Advance buffer pointer for manually written bytes 74 | 75 | Example for DMA transfer from memory 76 | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ 77 | 78 | This is an example showing pseudo code for implementing data transfer using DMA with zero-copy overhead. 79 | For read operation purposes, application gets direct access to LwRB read pointer and length of contiguous memory. 80 | 81 | It is assumed that after DMA transfer completes, interrupt is generated (embedded system) and buffer is skipped in the interrupt. 82 | 83 | .. note:: 84 | 85 | Buffer skip operation is used to mark sent data as processed and to free memory for new writes to buffer 86 | 87 | .. literalinclude:: ../examples_src/example_dma_skip.c 88 | :language: c 89 | :linenos: 90 | :caption: DMA usage with buffer 91 | 92 | .. tip:: 93 | Check `STM32 UART DMA TX RX Github `_ repository for use cases. 94 | 95 | .. toctree:: 96 | :maxdepth: 2 97 | -------------------------------------------------------------------------------- /docs/user-manual/index.rst: -------------------------------------------------------------------------------- 1 | .. _um: 2 | 3 | User manual 4 | =========== 5 | 6 | .. toctree:: 7 | :maxdepth: 2 8 | 9 | how-it-works 10 | events 11 | hw-dma-usage 12 | thread-safety -------------------------------------------------------------------------------- /docs/user-manual/thread-safety.rst: -------------------------------------------------------------------------------- 1 | .. _thread_safety: 2 | 3 | Thread safety 4 | ============= 5 | 6 | Ring buffers are effectively used in embedded systems with or without operating systems. 7 | Common problem most of implementations have, is linked to multi-thread environment (when using OS) or reading/writing from/to interrupts. 8 | Question becomes *What happens if I write to buffer while another thread is reading from it?* 9 | 10 | One of the main requirements (beside being lightweight) of *LwRB* was to allow *read-while-write* or *write-while-read* operations. 11 | This is achieved only when there is single write entry point and single read exit point. 12 | 13 | .. figure:: ../static/images/buff_thread_safety_single_read_write.svg 14 | :align: center 15 | :alt: Write and read operation with single entry and exit points 16 | 17 | Write and read operation with single entry and exit points 18 | 19 | Often called and used as *pipe* to write (for example) raw data to the buffer allowing another task 20 | to process the data from another thread. 21 | 22 | .. note:: 23 | No race-condition is introduced when application uses LwRB with single write entry and single read exit point. 24 | LwRB uses *C11* standard ``stdatomic.h`` library to ensure read and write operations are race-free for any platform supporting C11 and its respected atomic library. 25 | 26 | Thread (or interrupt) safety, with one entry and one exit points, is achieved by storing actual buffer read and write pointer variables to the local ones before performing any calculation. 27 | Therefore multiple *conditional* checks are guaranteed to be performed on the same local variables, even if actual buffer pointers get modified. 28 | 29 | * Read pointer could get changed by interrupt or another thread when application tries to write to buffer 30 | * Write pointer could get changed by interrupt or another thread when application ties to read from buffer 31 | 32 | .. note:: 33 | Even single entry and single exit points may introduce race condition, especially on smaller system, such as 8-bit or 16-bit system, or in general, 34 | where arbitrary type (normaly `size_t`) is `sizeof(type) > architecture_size`. 35 | This is solved by C11 atomic library, that ensures atomic reads and writes to key structure members. 36 | 37 | Thread safety gets completely broken when application does one of the following: 38 | 39 | * Uses multiple write entry points to the single LwRB instance 40 | * Uses multiple read exit points to the single LwRB instance 41 | * Uses multiple read/write exit/entry points to the same LwRB instance 42 | 43 | .. figure:: ../static/images/buff_thread_safety_2_thread_write.svg 44 | :align: center 45 | :alt: Write operation to same LwRB instance from 2 threads 46 | 47 | Write operation to same LwRB instance from 2 threads. 48 | Write protection is necessary to ensure thread safety. 49 | 50 | .. figure:: ../static/images/buff_thread_safety_2_main_irq_write.svg 51 | :align: center 52 | :alt: Write operation to same LwRB instance from main loop and interrupt context 53 | 54 | Write operation to same LwRB instance from main loop and interrupt context. 55 | Write protection is necessary to ensure thread safety. 56 | 57 | .. figure:: ../static/images/buff_thread_safety_2_thread_read.svg 58 | :align: center 59 | :alt: Read operation from same LwRB instance from 2 threads 60 | 61 | Read operation from same LwRB instance from 2 threads. 62 | Read protection is necessary to ensure thread safety. 63 | 64 | .. figure:: ../static/images/buff_thread_safety_2_thread_read_write.svg 65 | :align: center 66 | :alt: Read from and write to operations using LwRB instance from multiple threads 67 | 68 | Read and write operations are executed from multiple threads. 69 | Both, read and write, operations require exclusive access. 70 | 71 | Above use cases are examples when thread safety gets broken. Application must ensure 72 | exclusive access only to the part in *dashed-red* rectangle. 73 | 74 | .. literalinclude:: ../examples_src/example_thread_safety.c 75 | :language: c 76 | :linenos: 77 | :caption: Thread safety example 78 | 79 | Read and write operations can be used simultaneously hence it is perfectly valid if 80 | access is granted to *read* operation while *write* operation from one thread takes place. 81 | 82 | .. note:: 83 | ``2`` different mutexes are used for read and write due to the implementation, 84 | allowing application to use buffer in *read-while-write* and *write-while-read* mode. 85 | Mutexes are used to prevent *write-while-write* and *read-while-read* operations respectively 86 | 87 | .. tip:: 88 | For *multi-entry-point-single-exit-point* use case, *read* mutex is not necessary. 89 | For *single-entry-point-multi-exit-point* use case, *write* mutex is not necessary. 90 | 91 | .. tip:: 92 | Functions considered as *read* operation are ``read``, ``skip``, ``peek`` and ``linear read``. 93 | Functions considered as *write* operation are ``write``, ``advance`` and ``linear write``. 94 | 95 | .. toctree:: 96 | :maxdepth: 2 97 | 98 | Atomicity 99 | ========= 100 | 101 | While thread-safety concepts are very important, depending on the system architecture and variable sizes (and hardware cache), 102 | application must also ensure that all the writes and reads to the internal variables are executed in atomic manner. 103 | 104 | Especially critical case is when read/write from/to variable isn't ``1`` cycle on specific architecture (for instance 32-bit variable on 8-bit CPU). 105 | 106 | Library (in its default configuration) uses ``stdatomic`` feature from *C11* language, and relies on a compiler to properly 107 | generate necessary calls to make sure, all reads and writes are atomic. 108 | 109 | .. note:: 110 | Atomicity is required even if ring buffer is configured in *fifo* mode, with single write point and single read point. 111 | 112 | .. tip:: 113 | You can disable atomic operations in the library, by defining ``LWRB_DISABLE_ATOMIC`` global macro (typically with ``-D`` compiler option). 114 | It is then up to the developer to make sure architecture properly handles atomic operations. 115 | -------------------------------------------------------------------------------- /library.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "LwRB", 3 | "version": "3.2.0", 4 | "description": "Lightweight general purpose ring buffer with optimizations for embedded systems", 5 | "keywords": "buffer, ring buffer, library, rb, cyclic", 6 | "repository": { 7 | "type": "git", 8 | "url": "https://github.com/MaJerle/lwrb.git" 9 | }, 10 | "authors": [ 11 | { 12 | "name": "Tilen Majerle", 13 | "email": "tilen@majerle.eu", 14 | "url": "https://majerle.eu" 15 | } 16 | ], 17 | "license": "MIT", 18 | "homepage": "https://github.com/MaJerle/lwrb", 19 | "dependencies": {}, 20 | "frameworks": "*", 21 | "platforms": "*", 22 | "export": { 23 | "exclude": [ 24 | ".github", 25 | "dev", 26 | "docs", 27 | "**/.vs", 28 | "**/Debug", 29 | "build", 30 | "**/build" 31 | ] 32 | }, 33 | "build": { 34 | "includeDir": "lwrb/src/include", 35 | "srcDir": "lwrb/src/lwrb" 36 | } 37 | } -------------------------------------------------------------------------------- /lwrb/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | cmake_minimum_required(VERSION 3.22) 2 | 3 | include(${CMAKE_CURRENT_LIST_DIR}/library.cmake) 4 | -------------------------------------------------------------------------------- /lwrb/library.cmake: -------------------------------------------------------------------------------- 1 | # 2 | # LIB_PREFIX: LWRB 3 | # 4 | # This file provides set of variables for end user 5 | # and also generates one (or more) libraries, that can be added to the project using target_link_libraries(...) 6 | # 7 | # Before this file is included to the root CMakeLists file (using include() function), user can set some variables: 8 | # 9 | # LWRB_COMPILE_OPTIONS: If defined, it provide compiler options for generated library. 10 | # LWRB_COMPILE_DEFINITIONS: If defined, it provides "-D" definitions to the library build 11 | # 12 | 13 | # Custom include directory 14 | set(LWRB_CUSTOM_INC_DIR ${CMAKE_CURRENT_BINARY_DIR}/lib_inc) 15 | 16 | # Library core sources 17 | set(lwrb_core_SRCS 18 | ${CMAKE_CURRENT_LIST_DIR}/src/lwrb/lwrb.c 19 | ) 20 | 21 | # Library extended sources 22 | set(lwrb_ex_SRCS 23 | ${CMAKE_CURRENT_LIST_DIR}/src/lwrb/lwrb_ex.c 24 | ) 25 | 26 | # Setup include directories 27 | set(lwrb_include_DIRS 28 | ${CMAKE_CURRENT_LIST_DIR}/src/include 29 | ${LWPKT_CUSTOM_INC_DIR} 30 | ) 31 | 32 | # Register library to the system 33 | add_library(lwrb) 34 | target_sources(lwrb PRIVATE ${lwrb_core_SRCS}) 35 | target_include_directories(lwrb PUBLIC ${lwrb_include_DIRS}) 36 | target_compile_options(lwrb PRIVATE ${LWRB_COMPILE_OPTIONS}) 37 | target_compile_definitions(lwrb PRIVATE ${LWRB_COMPILE_DEFINITIONS}) 38 | 39 | # Register extended part 40 | add_library(lwrb_ex) 41 | target_sources(lwrb_ex PRIVATE ${lwrb_ex_SRCS}) 42 | target_include_directories(lwrb_ex PUBLIC ${lwrb_include_DIRS}) 43 | target_compile_options(lwrb_ex PRIVATE ${LWRB_COMPILE_OPTIONS}) 44 | target_compile_definitions(lwrb_ex PRIVATE ${LWRB_COMPILE_DEFINITIONS} LWRB_EXTENDED) 45 | target_link_libraries(lwrb_ex PUBLIC lwrb) 46 | -------------------------------------------------------------------------------- /lwrb/src/include/lwrb/lwrb.h: -------------------------------------------------------------------------------- 1 | /** 2 | * \file lwrb.h 3 | * \brief LwRB - Lightweight ring buffer 4 | */ 5 | 6 | /* 7 | * Copyright (c) 2024 Tilen MAJERLE 8 | * 9 | * Permission is hereby granted, free of charge, to any person 10 | * obtaining a copy of this software and associated documentation 11 | * files (the "Software"), to deal in the Software without restriction, 12 | * including without limitation the rights to use, copy, modify, merge, 13 | * publish, distribute, sublicense, and/or sell copies of the Software, 14 | * and to permit persons to whom the Software is furnished to do so, 15 | * subject to the following conditions: 16 | * 17 | * The above copyright notice and this permission notice shall be 18 | * included in all copies or substantial portions of the Software. 19 | * 20 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, 21 | * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES 22 | * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE 23 | * AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT 24 | * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, 25 | * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING 26 | * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR 27 | * OTHER DEALINGS IN THE SOFTWARE. 28 | * 29 | * This file is part of LwRB - Lightweight ring buffer library. 30 | * 31 | * Author: Tilen MAJERLE 32 | * Version: v3.2.0 33 | */ 34 | #ifndef LWRB_HDR_H 35 | #define LWRB_HDR_H 36 | 37 | #include 38 | #include 39 | #include 40 | 41 | #ifdef __cplusplus 42 | extern "C" { 43 | #endif /* __cplusplus */ 44 | 45 | /** 46 | * \defgroup LWRB Lightweight ring buffer manager 47 | * \brief Lightweight ring buffer manager 48 | * \{ 49 | */ 50 | 51 | #if !defined(LWRB_DISABLE_ATOMIC) || __DOXYGEN__ 52 | #include 53 | 54 | /** 55 | * \brief Atomic type for size variable. 56 | * Default value is set to be `unsigned 32-bits` type 57 | */ 58 | typedef atomic_ulong lwrb_sz_atomic_t; 59 | 60 | /** 61 | * \brief Size variable for all library operations. 62 | * Default value is set to be `unsigned 32-bits` type 63 | */ 64 | typedef unsigned long lwrb_sz_t; 65 | #else 66 | typedef unsigned long lwrb_sz_atomic_t; 67 | typedef unsigned long lwrb_sz_t; 68 | #endif 69 | 70 | /** 71 | * \brief Event type for buffer operations 72 | */ 73 | typedef enum { 74 | LWRB_EVT_READ, /*!< Read event */ 75 | LWRB_EVT_WRITE, /*!< Write event */ 76 | LWRB_EVT_RESET, /*!< Reset event */ 77 | } lwrb_evt_type_t; 78 | 79 | /** 80 | * \brief Buffer structure forward declaration 81 | */ 82 | struct lwrb; 83 | 84 | /** 85 | * \brief Event callback function type 86 | * \param[in] buff: Buffer handle for event 87 | * \param[in] evt: Event type 88 | * \param[in] bp: Number of bytes written or read (when used), depends on event type 89 | */ 90 | typedef void (*lwrb_evt_fn)(struct lwrb* buff, lwrb_evt_type_t evt, lwrb_sz_t bp); 91 | 92 | /* List of flags */ 93 | #define LWRB_FLAG_READ_ALL ((uint16_t)0x0001) 94 | #define LWRB_FLAG_WRITE_ALL ((uint16_t)0x0001) 95 | 96 | /** 97 | * \brief Buffer structure 98 | */ 99 | typedef struct lwrb { 100 | uint8_t* buff; /*!< Pointer to buffer data. Buffer is considered initialized when `buff != NULL` and `size > 0` */ 101 | lwrb_sz_t size; /*!< Size of buffer data. Size of actual buffer is `1` byte less than value holds */ 102 | lwrb_sz_atomic_t r_ptr; /*!< Next read pointer. 103 | Buffer is considered empty when `r == w` and full when `w == r - 1` */ 104 | lwrb_sz_atomic_t w_ptr; /*!< Next write pointer. 105 | Buffer is considered empty when `r == w` and full when `w == r - 1` */ 106 | lwrb_evt_fn evt_fn; /*!< Pointer to event callback function */ 107 | void* arg; /*!< Event custom user argument */ 108 | } lwrb_t; 109 | 110 | uint8_t lwrb_init(lwrb_t* buff, void* buffdata, lwrb_sz_t size); 111 | uint8_t lwrb_is_ready(lwrb_t* buff); 112 | void lwrb_free(lwrb_t* buff); 113 | void lwrb_reset(lwrb_t* buff); 114 | void lwrb_set_evt_fn(lwrb_t* buff, lwrb_evt_fn fn); 115 | void lwrb_set_arg(lwrb_t* buff, void* arg); 116 | void* lwrb_get_arg(lwrb_t* buff); 117 | 118 | /* Read/Write functions */ 119 | lwrb_sz_t lwrb_write(lwrb_t* buff, const void* data, lwrb_sz_t btw); 120 | lwrb_sz_t lwrb_read(lwrb_t* buff, void* data, lwrb_sz_t btr); 121 | lwrb_sz_t lwrb_peek(const lwrb_t* buff, lwrb_sz_t skip_count, void* data, lwrb_sz_t btp); 122 | 123 | /* Extended read/write functions */ 124 | uint8_t lwrb_write_ex(lwrb_t* buff, const void* data, lwrb_sz_t btw, lwrb_sz_t* bwritten, uint16_t flags); 125 | uint8_t lwrb_read_ex(lwrb_t* buff, void* data, lwrb_sz_t btr, lwrb_sz_t* bread, uint16_t flags); 126 | 127 | /* Buffer size information */ 128 | lwrb_sz_t lwrb_get_free(const lwrb_t* buff); 129 | lwrb_sz_t lwrb_get_full(const lwrb_t* buff); 130 | 131 | /* Read data block management */ 132 | void* lwrb_get_linear_block_read_address(const lwrb_t* buff); 133 | lwrb_sz_t lwrb_get_linear_block_read_length(const lwrb_t* buff); 134 | lwrb_sz_t lwrb_skip(lwrb_t* buff, lwrb_sz_t len); 135 | 136 | /* Write data block management */ 137 | void* lwrb_get_linear_block_write_address(const lwrb_t* buff); 138 | lwrb_sz_t lwrb_get_linear_block_write_length(const lwrb_t* buff); 139 | lwrb_sz_t lwrb_advance(lwrb_t* buff, lwrb_sz_t len); 140 | 141 | /* Search in buffer */ 142 | uint8_t lwrb_find(const lwrb_t* buff, const void* bts, lwrb_sz_t len, lwrb_sz_t start_offset, lwrb_sz_t* found_idx); 143 | lwrb_sz_t lwrb_overwrite(lwrb_t* buff, const void* data, lwrb_sz_t btw); 144 | lwrb_sz_t lwrb_move(lwrb_t* dest, lwrb_t* src); 145 | 146 | /** 147 | * \} 148 | */ 149 | 150 | #ifdef __cplusplus 151 | } 152 | #endif /* __cplusplus */ 153 | 154 | #endif /* LWRB_HDR_H */ 155 | -------------------------------------------------------------------------------- /lwrb/src/lwrb/lwrb.c: -------------------------------------------------------------------------------- 1 | /** 2 | * \file lwrb.c 3 | * \brief Lightweight ring buffer 4 | */ 5 | 6 | /* 7 | * Copyright (c) 2024 Tilen MAJERLE 8 | * 9 | * Permission is hereby granted, free of charge, to any person 10 | * obtaining a copy of this software and associated documentation 11 | * files (the "Software"), to deal in the Software without restriction, 12 | * including without limitation the rights to use, copy, modify, merge, 13 | * publish, distribute, sublicense, and/or sell copies of the Software, 14 | * and to permit persons to whom the Software is furnished to do so, 15 | * subject to the following conditions: 16 | * 17 | * The above copyright notice and this permission notice shall be 18 | * included in all copies or substantial portions of the Software. 19 | * 20 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, 21 | * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES 22 | * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE 23 | * AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT 24 | * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, 25 | * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING 26 | * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR 27 | * OTHER DEALINGS IN THE SOFTWARE. 28 | * 29 | * This file is part of LwRB - Lightweight ring buffer library. 30 | * 31 | * Author: Tilen MAJERLE 32 | * Version: v3.2.0 33 | */ 34 | #include "lwrb/lwrb.h" 35 | 36 | /* Memory set and copy functions */ 37 | #define BUF_MEMSET memset 38 | #define BUF_MEMCPY memcpy 39 | 40 | #define BUF_IS_VALID(b) ((b) != NULL && (b)->buff != NULL && (b)->size > 0) 41 | #define BUF_MIN(x, y) ((x) < (y) ? (x) : (y)) 42 | #define BUF_MAX(x, y) ((x) > (y) ? (x) : (y)) 43 | #define BUF_SEND_EVT(b, type, bp) \ 44 | do { \ 45 | if ((b)->evt_fn != NULL) { \ 46 | (b)->evt_fn((void*)(b), (type), (bp)); \ 47 | } \ 48 | } while (0) 49 | 50 | /* Optional atomic opeartions */ 51 | #ifdef LWRB_DISABLE_ATOMIC 52 | #define LWRB_INIT(var, val) (var) = (val) 53 | #define LWRB_LOAD(var, type) (var) 54 | #define LWRB_STORE(var, val, type) (var) = (val) 55 | #else 56 | #define LWRB_INIT(var, val) atomic_init(&(var), (val)) 57 | #define LWRB_LOAD(var, type) atomic_load_explicit(&(var), (type)) 58 | #define LWRB_STORE(var, val, type) atomic_store_explicit(&(var), (val), (type)) 59 | #endif 60 | 61 | /** 62 | * \brief Initialize buffer handle to default values with size and buffer data array 63 | * \param[in] buff: Ring buffer instance 64 | * \param[in] buffdata: Pointer to memory to use as buffer data 65 | * \param[in] size: Size of `buffdata` in units of bytes 66 | * Maximum number of bytes buffer can hold is `size - 1` 67 | * \return `1` on success, `0` otherwise 68 | */ 69 | uint8_t 70 | lwrb_init(lwrb_t* buff, void* buffdata, lwrb_sz_t size) { 71 | if (buff == NULL || buffdata == NULL || size == 0) { 72 | return 0; 73 | } 74 | 75 | buff->evt_fn = NULL; 76 | buff->size = size; 77 | buff->buff = buffdata; 78 | LWRB_INIT(buff->w_ptr, 0); 79 | LWRB_INIT(buff->r_ptr, 0); 80 | return 1; 81 | } 82 | 83 | /** 84 | * \brief Check if buff is initialized and ready to use 85 | * \param[in] buff: Ring buffer instance 86 | * \return `1` if ready, `0` otherwise 87 | */ 88 | uint8_t 89 | lwrb_is_ready(lwrb_t* buff) { 90 | return BUF_IS_VALID(buff); 91 | } 92 | 93 | /** 94 | * \brief Free buffer memory 95 | * \note Since implementation does not use dynamic allocation, 96 | * it just sets buffer handle to `NULL` 97 | * \param[in] buff: Ring buffer instance 98 | */ 99 | void 100 | lwrb_free(lwrb_t* buff) { 101 | if (BUF_IS_VALID(buff)) { 102 | buff->buff = NULL; 103 | } 104 | } 105 | 106 | /** 107 | * \brief Set event function callback for different buffer operations 108 | * \param[in] buff: Ring buffer instance 109 | * \param[in] evt_fn: Callback function 110 | */ 111 | void 112 | lwrb_set_evt_fn(lwrb_t* buff, lwrb_evt_fn evt_fn) { 113 | if (BUF_IS_VALID(buff)) { 114 | buff->evt_fn = evt_fn; 115 | } 116 | } 117 | 118 | /** 119 | * \brief Set custom buffer argument, that can be retrieved in the event function 120 | * \param[in] buff: Ring buffer instance 121 | * \param[in] arg: Custom user argument 122 | */ 123 | void 124 | lwrb_set_arg(lwrb_t* buff, void* arg) { 125 | if (BUF_IS_VALID(buff)) { 126 | buff->arg = arg; 127 | } 128 | } 129 | 130 | /** 131 | * \brief Get custom buffer argument, previously set with \ref lwrb_set_arg 132 | * \param[in] buff: Ring buffer instance 133 | * \return User argument, previously set with \ref lwrb_set_arg 134 | */ 135 | void* 136 | lwrb_get_arg(lwrb_t* buff) { 137 | return buff != NULL ? buff->arg : NULL; 138 | } 139 | 140 | /** 141 | * \brief Write data to buffer. 142 | * Copies data from `data` array to buffer and advances the write pointer for a maximum of `btw` number of bytes. 143 | * 144 | * It copies less if there is less memory available in the buffer. 145 | * User must check the return value of the function and compare it to 146 | * the requested write length, to determine if everything has been written 147 | * 148 | * \note Use \ref lwrb_write_ex for more advanced usage 149 | * 150 | * \param[in] buff: Ring buffer instance 151 | * \param[in] data: Pointer to data to write into buffer 152 | * \param[in] btw: Number of bytes to write 153 | * \return Number of bytes written to buffer. 154 | * When returned value is less than `btw`, there was no enough memory available 155 | * to copy full data array. 156 | */ 157 | lwrb_sz_t 158 | lwrb_write(lwrb_t* buff, const void* data, lwrb_sz_t btw) { 159 | lwrb_sz_t written = 0; 160 | 161 | if (lwrb_write_ex(buff, data, btw, &written, 0)) { 162 | return written; 163 | } 164 | return 0; 165 | } 166 | 167 | /** 168 | * \brief Write extended functionality 169 | * 170 | * \param buff: Ring buffer instance 171 | * \param data: Pointer to data to write into buffer 172 | * \param btw: Number of bytes to write 173 | * \param bwritten: Output pointer to write number of bytes written into the buffer 174 | * \param flags: Optional flags. 175 | * \ref LWRB_FLAG_WRITE_ALL: Request to write all data (up to btw). 176 | * Will early return if no memory available 177 | * \return `1` if write operation OK, `0` otherwise 178 | */ 179 | uint8_t 180 | lwrb_write_ex(lwrb_t* buff, const void* data, lwrb_sz_t btw, lwrb_sz_t* bwritten, uint16_t flags) { 181 | lwrb_sz_t tocopy = 0, free = 0, w_ptr = 0; 182 | const uint8_t* d_ptr = data; 183 | 184 | if (!BUF_IS_VALID(buff) || data == NULL || btw == 0) { 185 | return 0; 186 | } 187 | 188 | /* Calculate maximum number of bytes available to write */ 189 | free = lwrb_get_free(buff); 190 | /* If no memory, or if user wants to write ALL data but no enough space, exit early */ 191 | if (free == 0 || (free < btw && (flags & LWRB_FLAG_WRITE_ALL))) { 192 | return 0; 193 | } 194 | btw = BUF_MIN(free, btw); 195 | w_ptr = LWRB_LOAD(buff->w_ptr, memory_order_acquire); 196 | 197 | /* Step 1: Write data to linear part of buffer */ 198 | tocopy = BUF_MIN(buff->size - w_ptr, btw); 199 | BUF_MEMCPY(&buff->buff[w_ptr], d_ptr, tocopy); 200 | d_ptr += tocopy; 201 | w_ptr += tocopy; 202 | btw -= tocopy; 203 | 204 | /* Step 2: Write data to beginning of buffer (overflow part) */ 205 | if (btw > 0) { 206 | BUF_MEMCPY(buff->buff, d_ptr, btw); 207 | w_ptr = btw; 208 | } 209 | 210 | /* Step 3: Check end of buffer */ 211 | if (w_ptr >= buff->size) { 212 | w_ptr = 0; 213 | } 214 | 215 | /* 216 | * Write final value to the actual running variable. 217 | * This is to ensure no read operation can access intermediate data 218 | */ 219 | LWRB_STORE(buff->w_ptr, w_ptr, memory_order_release); 220 | 221 | BUF_SEND_EVT(buff, LWRB_EVT_WRITE, tocopy + btw); 222 | if (bwritten != NULL) { 223 | *bwritten = tocopy + btw; 224 | } 225 | return 1; 226 | } 227 | 228 | /** 229 | * \brief Read data from buffer. 230 | * Copies data from `data` array to buffer and advances the read pointer for a maximum of `btr` number of bytes. 231 | * 232 | * It copies less if there is less data available in the buffer. 233 | * 234 | * \note Use \ref lwrb_read_ex for more advanced usage 235 | * 236 | * \param[in] buff: Ring buffer instance 237 | * \param[out] data: Pointer to output memory to copy buffer data to 238 | * \param[in] btr: Number of bytes to read 239 | * \return Number of bytes read and copied to data array 240 | */ 241 | lwrb_sz_t 242 | lwrb_read(lwrb_t* buff, void* data, lwrb_sz_t btr) { 243 | lwrb_sz_t read = 0; 244 | 245 | if (lwrb_read_ex(buff, data, btr, &read, 0)) { 246 | return read; 247 | } 248 | return 0; 249 | } 250 | 251 | /** 252 | * \brief Read extended functionality 253 | * 254 | * \param buff: Ring buffer instance 255 | * \param data: Pointer to memory to write read data from buffer 256 | * \param btr: Number of bytes to read 257 | * \param bread: Output pointer to write number of bytes read from buffer and written to the 258 | * output `data` variable 259 | * \param flags: Optional flags 260 | * \ref LWRB_FLAG_READ_ALL: Request to read all data (up to btr). 261 | * Will early return if no enough bytes in the buffer 262 | * \return `1` if read operation OK, `0` otherwise 263 | */ 264 | uint8_t 265 | lwrb_read_ex(lwrb_t* buff, void* data, lwrb_sz_t btr, lwrb_sz_t* bread, uint16_t flags) { 266 | lwrb_sz_t tocopy = 0, full = 0, r_ptr = 0; 267 | uint8_t* d_ptr = data; 268 | 269 | if (!BUF_IS_VALID(buff) || data == NULL || btr == 0) { 270 | return 0; 271 | } 272 | 273 | /* Calculate maximum number of bytes available to read */ 274 | full = lwrb_get_full(buff); 275 | if (full == 0 || (full < btr && (flags & LWRB_FLAG_READ_ALL))) { 276 | return 0; 277 | } 278 | btr = BUF_MIN(full, btr); 279 | r_ptr = LWRB_LOAD(buff->r_ptr, memory_order_acquire); 280 | 281 | /* Step 1: Read data from linear part of buffer */ 282 | tocopy = BUF_MIN(buff->size - r_ptr, btr); 283 | BUF_MEMCPY(d_ptr, &buff->buff[r_ptr], tocopy); 284 | d_ptr += tocopy; 285 | r_ptr += tocopy; 286 | btr -= tocopy; 287 | 288 | /* Step 2: Read data from beginning of buffer (overflow part) */ 289 | if (btr > 0) { 290 | BUF_MEMCPY(d_ptr, buff->buff, btr); 291 | r_ptr = btr; 292 | } 293 | 294 | /* Step 3: Check end of buffer */ 295 | if (r_ptr >= buff->size) { 296 | r_ptr = 0; 297 | } 298 | 299 | /* 300 | * Write final value to the actual running variable. 301 | * This is to ensure no write operation can access intermediate data 302 | */ 303 | LWRB_STORE(buff->r_ptr, r_ptr, memory_order_release); 304 | 305 | BUF_SEND_EVT(buff, LWRB_EVT_READ, tocopy + btr); 306 | if (bread != NULL) { 307 | *bread = tocopy + btr; 308 | } 309 | return 1; 310 | } 311 | 312 | /** 313 | * \brief Read from buffer without changing read pointer (peek only) 314 | * \param[in] buff: Ring buffer instance 315 | * \param[in] skip_count: Number of bytes to skip before reading data 316 | * \param[out] data: Pointer to output memory to copy buffer data to 317 | * \param[in] btp: Number of bytes to peek 318 | * \return Number of bytes peeked and written to output array 319 | */ 320 | lwrb_sz_t 321 | lwrb_peek(const lwrb_t* buff, lwrb_sz_t skip_count, void* data, lwrb_sz_t btp) { 322 | lwrb_sz_t full = 0, tocopy = 0, r_ptr = 0; 323 | uint8_t* d_ptr = data; 324 | 325 | if (!BUF_IS_VALID(buff) || data == NULL || btp == 0) { 326 | return 0; 327 | } 328 | 329 | /* 330 | * Calculate maximum number of bytes available to read 331 | * and check if we can even fit to it 332 | */ 333 | full = lwrb_get_full(buff); 334 | if (skip_count >= full) { 335 | return 0; 336 | } 337 | r_ptr = LWRB_LOAD(buff->r_ptr, memory_order_relaxed); 338 | r_ptr += skip_count; 339 | full -= skip_count; 340 | if (r_ptr >= buff->size) { 341 | r_ptr -= buff->size; 342 | } 343 | 344 | /* Check maximum number of bytes available to read after skip */ 345 | btp = BUF_MIN(full, btp); 346 | if (btp == 0) { 347 | return 0; 348 | } 349 | 350 | /* Step 1: Read data from linear part of buffer */ 351 | tocopy = BUF_MIN(buff->size - r_ptr, btp); 352 | BUF_MEMCPY(d_ptr, &buff->buff[r_ptr], tocopy); 353 | d_ptr += tocopy; 354 | btp -= tocopy; 355 | 356 | /* Step 2: Read data from beginning of buffer (overflow part) */ 357 | if (btp > 0) { 358 | BUF_MEMCPY(d_ptr, buff->buff, btp); 359 | } 360 | return tocopy + btp; 361 | } 362 | 363 | /** 364 | * \brief Get available size in buffer for write operation 365 | * \param[in] buff: Ring buffer instance 366 | * \return Number of free bytes in memory 367 | */ 368 | lwrb_sz_t 369 | lwrb_get_free(const lwrb_t* buff) { 370 | lwrb_sz_t size = 0, w_ptr = 0, r_ptr = 0; 371 | 372 | if (!BUF_IS_VALID(buff)) { 373 | return 0; 374 | } 375 | 376 | /* 377 | * Copy buffer pointers to local variables with atomic access. 378 | * 379 | * To ensure thread safety (only when in single-entry, single-exit FIFO mode use case), 380 | * it is important to write buffer r and w values to local w and r variables. 381 | * 382 | * Local variables will ensure below if statements will always use the same value, 383 | * even if buff->w or buff->r get changed during interrupt processing. 384 | * 385 | * They may change during load operation, important is that 386 | * they do not change during if-else operations following these assignments. 387 | * 388 | * lwrb_get_free is only called for write purpose, and when in FIFO mode, then: 389 | * - buff->w pointer will not change by another process/interrupt because we are in write mode just now 390 | * - buff->r pointer may change by another process. If it gets changed after buff->r has been loaded to local variable, 391 | * buffer will see "free size" less than it actually is. This is not a problem, application can 392 | * always try again to write more data to remaining free memory that was read just during copy operation 393 | */ 394 | w_ptr = LWRB_LOAD(buff->w_ptr, memory_order_relaxed); 395 | r_ptr = LWRB_LOAD(buff->r_ptr, memory_order_relaxed); 396 | 397 | if (w_ptr >= r_ptr) { 398 | size = buff->size - (w_ptr - r_ptr); 399 | } else { 400 | size = r_ptr - w_ptr; 401 | } 402 | 403 | /* Buffer free size is always 1 less than actual size */ 404 | return size - 1; 405 | } 406 | 407 | /** 408 | * \brief Get number of bytes currently available in buffer 409 | * \param[in] buff: Ring buffer instance 410 | * \return Number of bytes ready to be read 411 | */ 412 | lwrb_sz_t 413 | lwrb_get_full(const lwrb_t* buff) { 414 | lwrb_sz_t size = 0, w_ptr = 0, r_ptr = 0; 415 | 416 | if (!BUF_IS_VALID(buff)) { 417 | return 0; 418 | } 419 | 420 | /* 421 | * Copy buffer pointers to local variables. 422 | * 423 | * To ensure thread safety (only when in single-entry, single-exit FIFO mode use case), 424 | * it is important to write buffer r and w values to local w and r variables. 425 | * 426 | * Local variables will ensure below if statements will always use the same value, 427 | * even if buff->w or buff->r get changed during interrupt processing. 428 | * 429 | * They may change during load operation, important is that 430 | * they do not change during if-else operations following these assignments. 431 | * 432 | * lwrb_get_full is only called for read purpose, and when in FIFO mode, then: 433 | * - buff->r pointer will not change by another process/interrupt because we are in read mode just now 434 | * - buff->w pointer may change by another process. If it gets changed after buff->w has been loaded to local variable, 435 | * buffer will see "full size" less than it really is. This is not a problem, application can 436 | * always try again to read more data from remaining full memory that was written just during copy operation 437 | */ 438 | w_ptr = LWRB_LOAD(buff->w_ptr, memory_order_relaxed); 439 | r_ptr = LWRB_LOAD(buff->r_ptr, memory_order_relaxed); 440 | 441 | if (w_ptr >= r_ptr) { 442 | size = w_ptr - r_ptr; 443 | } else { 444 | size = buff->size - (r_ptr - w_ptr); 445 | } 446 | return size; 447 | } 448 | 449 | /** 450 | * \brief Resets buffer to default values. Buffer size is not modified 451 | * \note This function is not thread safe. 452 | * When used, application must ensure there is no active read/write operation 453 | * \param[in] buff: Ring buffer instance 454 | */ 455 | void 456 | lwrb_reset(lwrb_t* buff) { 457 | if (BUF_IS_VALID(buff)) { 458 | LWRB_STORE(buff->w_ptr, 0, memory_order_release); 459 | LWRB_STORE(buff->r_ptr, 0, memory_order_release); 460 | BUF_SEND_EVT(buff, LWRB_EVT_RESET, 0); 461 | } 462 | } 463 | 464 | /** 465 | * \brief Get linear address for buffer for fast read 466 | * \param[in] buff: Ring buffer instance 467 | * \return Linear buffer start address 468 | */ 469 | void* 470 | lwrb_get_linear_block_read_address(const lwrb_t* buff) { 471 | lwrb_sz_t ptr = 0; 472 | 473 | if (!BUF_IS_VALID(buff)) { 474 | return NULL; 475 | } 476 | ptr = LWRB_LOAD(buff->r_ptr, memory_order_relaxed); 477 | return &buff->buff[ptr]; 478 | } 479 | 480 | /** 481 | * \brief Get length of linear block address before it overflows for read operation 482 | * \param[in] buff: Ring buffer instance 483 | * \return Linear buffer size in units of bytes for read operation 484 | */ 485 | lwrb_sz_t 486 | lwrb_get_linear_block_read_length(const lwrb_t* buff) { 487 | lwrb_sz_t len = 0, w_ptr = 0, r_ptr = 0; 488 | 489 | if (!BUF_IS_VALID(buff)) { 490 | return 0; 491 | } 492 | 493 | /* 494 | * Use temporary values in case they are changed during operations. 495 | * See lwrb_buff_free or lwrb_buff_full functions for more information why this is OK. 496 | */ 497 | w_ptr = LWRB_LOAD(buff->w_ptr, memory_order_relaxed); 498 | r_ptr = LWRB_LOAD(buff->r_ptr, memory_order_relaxed); 499 | 500 | if (w_ptr > r_ptr) { 501 | len = w_ptr - r_ptr; 502 | } else if (r_ptr > w_ptr) { 503 | len = buff->size - r_ptr; 504 | } else { 505 | len = 0; 506 | } 507 | return len; 508 | } 509 | 510 | /** 511 | * \brief Skip (ignore; advance read pointer) buffer data 512 | * Marks data as read in the buffer and increases free memory for up to `len` bytes 513 | * 514 | * \note Useful at the end of streaming transfer such as DMA 515 | * \param[in] buff: Ring buffer instance 516 | * \param[in] len: Number of bytes to skip and mark as read 517 | * \return Number of bytes skipped 518 | */ 519 | lwrb_sz_t 520 | lwrb_skip(lwrb_t* buff, lwrb_sz_t len) { 521 | lwrb_sz_t full = 0, r_ptr = 0; 522 | 523 | if (!BUF_IS_VALID(buff) || len == 0) { 524 | return 0; 525 | } 526 | 527 | full = lwrb_get_full(buff); 528 | len = BUF_MIN(len, full); 529 | r_ptr = LWRB_LOAD(buff->r_ptr, memory_order_acquire); 530 | r_ptr += len; 531 | if (r_ptr >= buff->size) { 532 | r_ptr -= buff->size; 533 | } 534 | LWRB_STORE(buff->r_ptr, r_ptr, memory_order_release); 535 | BUF_SEND_EVT(buff, LWRB_EVT_READ, len); 536 | return len; 537 | } 538 | 539 | /** 540 | * \brief Get linear address for buffer for fast write 541 | * \param[in] buff: Ring buffer instance 542 | * \return Linear buffer start address 543 | */ 544 | void* 545 | lwrb_get_linear_block_write_address(const lwrb_t* buff) { 546 | lwrb_sz_t ptr = 0; 547 | 548 | if (!BUF_IS_VALID(buff)) { 549 | return NULL; 550 | } 551 | ptr = LWRB_LOAD(buff->w_ptr, memory_order_relaxed); 552 | return &buff->buff[ptr]; 553 | } 554 | 555 | /** 556 | * \brief Get length of linear block address before it overflows for write operation 557 | * \param[in] buff: Ring buffer instance 558 | * \return Linear buffer size in units of bytes for write operation 559 | */ 560 | lwrb_sz_t 561 | lwrb_get_linear_block_write_length(const lwrb_t* buff) { 562 | lwrb_sz_t len = 0, w_ptr = 0, r_ptr = 0; 563 | 564 | if (!BUF_IS_VALID(buff)) { 565 | return 0; 566 | } 567 | 568 | /* 569 | * Use temporary values in case they are changed during operations. 570 | * See lwrb_buff_free or lwrb_buff_full functions for more information why this is OK. 571 | */ 572 | w_ptr = LWRB_LOAD(buff->w_ptr, memory_order_relaxed); 573 | r_ptr = LWRB_LOAD(buff->r_ptr, memory_order_relaxed); 574 | 575 | if (w_ptr >= r_ptr) { 576 | len = buff->size - w_ptr; 577 | /* 578 | * When read pointer is 0, 579 | * maximal length is one less as if too many bytes 580 | * are written, buffer would be considered empty again (r == w) 581 | */ 582 | if (r_ptr == 0) { 583 | /* 584 | * Cannot overflow: 585 | * - If r is not 0, statement does not get called 586 | * - buff->size cannot be 0 and if r is 0, len is greater 0 587 | */ 588 | --len; 589 | } 590 | } else { 591 | len = r_ptr - w_ptr - 1; 592 | } 593 | return len; 594 | } 595 | 596 | /** 597 | * \brief Advance write pointer in the buffer. 598 | * Similar to skip function but modifies write pointer instead of read 599 | * 600 | * \note Useful when hardware is writing to buffer and application needs to increase number 601 | * of bytes written to buffer by hardware 602 | * \param[in] buff: Ring buffer instance 603 | * \param[in] len: Number of bytes to advance 604 | * \return Number of bytes advanced for write operation 605 | */ 606 | lwrb_sz_t 607 | lwrb_advance(lwrb_t* buff, lwrb_sz_t len) { 608 | lwrb_sz_t free = 0, w_ptr = 0; 609 | 610 | if (!BUF_IS_VALID(buff) || len == 0) { 611 | return 0; 612 | } 613 | 614 | /* Use local variables before writing back to main structure */ 615 | free = lwrb_get_free(buff); 616 | len = BUF_MIN(len, free); 617 | w_ptr = LWRB_LOAD(buff->w_ptr, memory_order_acquire); 618 | w_ptr += len; 619 | if (w_ptr >= buff->size) { 620 | w_ptr -= buff->size; 621 | } 622 | LWRB_STORE(buff->w_ptr, w_ptr, memory_order_release); 623 | BUF_SEND_EVT(buff, LWRB_EVT_WRITE, len); 624 | return len; 625 | } 626 | 627 | /** 628 | * \brief Searches for a *needle* in an array, starting from given offset. 629 | * 630 | * \note This function is not thread-safe. 631 | * 632 | * \param buff: Ring buffer to search for needle in 633 | * \param bts: Constant byte array sequence to search for in a buffer 634 | * \param len: Length of the \arg bts array 635 | * \param start_offset: Start offset in the buffer 636 | * \param found_idx: Pointer to variable to write index in array where bts has been found 637 | * Must not be set to `NULL` 638 | * \return `1` if \arg bts found, `0` otherwise 639 | */ 640 | uint8_t 641 | lwrb_find(const lwrb_t* buff, const void* bts, lwrb_sz_t len, lwrb_sz_t start_offset, lwrb_sz_t* found_idx) { 642 | lwrb_sz_t full = 0, r_ptr = 0, buff_r_ptr = 0, max_x = 0; 643 | uint8_t found = 0; 644 | const uint8_t* needle = bts; 645 | 646 | if (!BUF_IS_VALID(buff) || needle == NULL || len == 0 || found_idx == NULL) { 647 | return 0; 648 | } 649 | *found_idx = 0; 650 | 651 | full = lwrb_get_full(buff); 652 | /* Verify initial conditions */ 653 | if (full < (len + start_offset)) { 654 | return 0; 655 | } 656 | 657 | /* Get actual buffer read pointer for this search */ 658 | buff_r_ptr = LWRB_LOAD(buff->r_ptr, memory_order_relaxed); 659 | 660 | /* Max number of for loops is buff_full - input_len - start_offset of buffer length */ 661 | max_x = full - len; 662 | for (lwrb_sz_t skip_x = start_offset; !found && skip_x <= max_x; ++skip_x) { 663 | found = 1; /* Found by default */ 664 | 665 | /* Prepare the starting point for reading */ 666 | r_ptr = buff_r_ptr + skip_x; 667 | if (r_ptr >= buff->size) { 668 | r_ptr -= buff->size; 669 | } 670 | 671 | /* Search in the buffer */ 672 | for (lwrb_sz_t idx = 0; idx < len; ++idx) { 673 | if (buff->buff[r_ptr] != needle[idx]) { 674 | found = 0; 675 | break; 676 | } 677 | if (++r_ptr >= buff->size) { 678 | r_ptr = 0; 679 | } 680 | } 681 | if (found) { 682 | *found_idx = skip_x; 683 | } 684 | } 685 | return found; 686 | } 687 | -------------------------------------------------------------------------------- /lwrb/src/lwrb/lwrb_ex.c: -------------------------------------------------------------------------------- 1 | /** 2 | * \file lwrb_ex.c 3 | * \brief Lightweight ring buffer - extended functions 4 | */ 5 | 6 | /* 7 | * Copyright (c) 2024 Tilen MAJERLE 8 | * 9 | * Permission is hereby granted, free of charge, to any person 10 | * obtaining a copy of this software and associated documentation 11 | * files (the "Software"), to deal in the Software without restriction, 12 | * including without limitation the rights to use, copy, modify, merge, 13 | * publish, distribute, sublicense, and/or sell copies of the Software, 14 | * and to permit persons to whom the Software is furnished to do so, 15 | * subject to the following conditions: 16 | * 17 | * The above copyright notice and this permission notice shall be 18 | * included in all copies or substantial portions of the Software. 19 | * 20 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, 21 | * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES 22 | * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE 23 | * AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT 24 | * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, 25 | * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING 26 | * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR 27 | * OTHER DEALINGS IN THE SOFTWARE. 28 | * 29 | * This file is part of LwRB - Lightweight ring buffer library. 30 | * 31 | * Author: Tilen MAJERLE 32 | * Version: v3.2.0 33 | */ 34 | #include "lwrb/lwrb.h" 35 | 36 | #if defined(LWRB_DEV) 37 | 38 | /* Do not build if development mode isn't enabled */ 39 | 40 | #define BUF_IS_VALID(b) ((b) != NULL && (b)->buff != NULL && (b)->size > 0) 41 | #define BUF_MIN(x, y) ((x) < (y) ? (x) : (y)) 42 | 43 | /** 44 | * \brief Writes data to buffer with overwrite function, if no enough space to hold 45 | * complete input data object. 46 | * \note Similar to \ref lwrb_write but overwrites 47 | * \param[in] buff: Buffer handle 48 | * \param[in] data: Data to write to ring buffer 49 | * \param[in] btw: Bytes To Write, length 50 | * \return Number of bytes written to buffer, will always return btw 51 | * \note Functionality is primary two parts, always writes some linear region, then 52 | * writes the wrap region if there is more data to write. The r indicator is advanced if w overtakes 53 | * it. This operation is a read op as well as a write op. For thread-safety mutexes may be desired, 54 | * see documentation. 55 | */ 56 | lwrb_sz_t 57 | lwrb_overwrite(lwrb_t* buff, const void* data, lwrb_sz_t btw) { 58 | lwrb_sz_t orig_btw = btw, max_cap; 59 | const uint8_t* d = data; 60 | 61 | if (!BUF_IS_VALID(buff) || data == NULL || btw == 0) { 62 | return 0; 63 | } 64 | 65 | /* Process complete input array */ 66 | max_cap = buff->size - 1; /* Maximum capacity buffer can hold */ 67 | if (btw > max_cap) { 68 | /* 69 | * When data to write is larger than max buffer capacity, 70 | * we can reset the buffer and simply write last part of 71 | * the input buffer. 72 | * 73 | * This is done here, by calculating remaining 74 | * length and then advancing to the end of input buffer 75 | */ 76 | d += btw - max_cap; /* Advance data */ 77 | btw = max_cap; /* Limit data to write */ 78 | lwrb_reset(buff); /* Reset buffer */ 79 | } else { 80 | /* 81 | * Bytes to write is less than capacity 82 | * We have to perform max one skip operation, 83 | * but only if free memory is less than 84 | * btw, otherwise we skip the operation 85 | * and only write the data. 86 | */ 87 | lwrb_sz_t f = lwrb_get_free(buff); 88 | if (f < btw) { 89 | lwrb_skip(buff, btw - f); 90 | } 91 | } 92 | lwrb_write(buff, d, btw); 93 | return orig_btw; 94 | } 95 | 96 | /** 97 | * \brief Move one ring buffer to another, up to the amount of data in the source, or amount 98 | * of data free in the destination. 99 | * \param[in] dest: Buffer handle that the copied data will be written to 100 | * \param[in] src: Buffer handle that the copied data will come from. 101 | * Source buffer will be effectively read upon operation. 102 | * \return Number of bytes written to destination buffer 103 | * \note This operation is a read op to the source, on success it will update the r index. 104 | * As well as a write op to the destination, and may update the w index. 105 | * For thread-safety mutexes may be desired, see documentation. 106 | */ 107 | lwrb_sz_t 108 | lwrb_move(lwrb_t* dest, lwrb_t* src) { 109 | lwrb_sz_t len_to_copy, len_to_copy_orig, src_full, dest_free; 110 | 111 | if (!BUF_IS_VALID(dest) || !BUF_IS_VALID(src)) { 112 | return 0; 113 | } 114 | src_full = lwrb_get_full(src); 115 | dest_free = lwrb_get_free(dest); 116 | len_to_copy = BUF_MIN(src_full, dest_free); 117 | len_to_copy_orig = len_to_copy; 118 | 119 | /* Calculations for available length to copy is done above. 120 | We safely assume operations inside loop will properly complete. */ 121 | while (len_to_copy > 0) { 122 | lwrb_sz_t max_seq_read, max_seq_write, op_len; 123 | const uint8_t* d_src; 124 | uint8_t* d_dst; 125 | 126 | /* Calculate data */ 127 | max_seq_read = lwrb_get_linear_block_read_length(src); 128 | max_seq_write = lwrb_get_linear_block_write_length(dest); 129 | op_len = BUF_MIN(max_seq_read, max_seq_write); 130 | op_len = BUF_MIN(len_to_copy, op_len); 131 | 132 | /* Get addresses */ 133 | d_src = lwrb_get_linear_block_read_address(src); 134 | d_dst = lwrb_get_linear_block_write_address(dest); 135 | 136 | /* Byte by byte copy */ 137 | for (lwrb_sz_t i = 0; i < op_len; ++i) { 138 | *d_dst++ = *d_src++; 139 | } 140 | 141 | lwrb_advance(dest, op_len); 142 | lwrb_skip(src, op_len); 143 | len_to_copy -= op_len; 144 | if (op_len == 0) { 145 | /* Hard error... */ 146 | return 0; 147 | } 148 | } 149 | return len_to_copy_orig; 150 | } 151 | 152 | #endif /* defined(LWRB_DEV) */ 153 | -------------------------------------------------------------------------------- /lwrb/test/.gitignore: -------------------------------------------------------------------------------- 1 | 2 | .vscode/ 3 | .cache/ 4 | build/ 5 | 6 | # Created by https://www.toptal.com/developers/gitignore/api/cmake,c 7 | # Edit at https://www.toptal.com/developers/gitignore?templates=cmake,c 8 | 9 | ### C ### 10 | # Prerequisites 11 | *.d 12 | 13 | # Object files 14 | *.o 15 | *.ko 16 | *.obj 17 | *.elf 18 | 19 | # Linker output 20 | *.ilk 21 | *.map 22 | *.exp 23 | 24 | # Precompiled Headers 25 | *.gch 26 | *.pch 27 | 28 | # Libraries 29 | *.lib 30 | *.a 31 | *.la 32 | *.lo 33 | 34 | # Shared objects (inc. Windows DLLs) 35 | *.dll 36 | *.so 37 | *.so.* 38 | *.dylib 39 | 40 | # Executables 41 | *.exe 42 | *.out 43 | *.app 44 | *.i*86 45 | *.x86_64 46 | *.hex 47 | 48 | # Debug files 49 | *.dSYM/ 50 | *.su 51 | *.idb 52 | *.pdb 53 | 54 | # Kernel Module Compile Results 55 | *.mod* 56 | *.cmd 57 | .tmp_versions/ 58 | modules.order 59 | Module.symvers 60 | Mkfile.old 61 | dkms.conf 62 | 63 | ### CMake ### 64 | CMakeLists.txt.user 65 | CMakeCache.txt 66 | CMakeFiles 67 | CMakeScripts 68 | Testing 69 | Makefile 70 | cmake_install.cmake 71 | install_manifest.txt 72 | compile_commands.json 73 | CTestTestfile.cmake 74 | _deps 75 | 76 | ### CMake Patch ### 77 | # External projects 78 | *-prefix/ 79 | 80 | # End of https://www.toptal.com/developers/gitignore/api/cmake,c -------------------------------------------------------------------------------- /lwrb/test/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | cmake_minimum_required(VERSION 3.10) 2 | project(lwrb-testing) 3 | 4 | set(THIRD_PARTY_DIR ${CMAKE_SOURCE_DIR}/../../third_party) 5 | set(UNITY_DIR ${THIRD_PARTY_DIR}/Unity) 6 | set(LWRB_DIR ${CMAKE_SOURCE_DIR}/../src) 7 | 8 | set(CMAKE_MODULE_PATH "${THIRD_PARTY_DIR}/sanitizers-cmake/cmake;${THIRD_PARTY_DIR}/cmake-modules" ${CMAKE_MODULE_PATH}) 9 | 10 | if(WITH_COVERAGE) 11 | find_program(LLVMCOV_PATH llvm-cov) 12 | if("${CMAKE_CXX_COMPILER_ID}" MATCHES "(Apple)?[Cc]lang") 13 | if(LLVMCOV_PATH) 14 | set(GCOV_PATH "${CMAKE_CURRENT_SOURCE_DIR}/llvm-cov.sh") 15 | endif(LLVMCOV_PATH) 16 | endif() 17 | include(CodeCoverage) 18 | append_coverage_compiler_flags() 19 | endif(WITH_COVERAGE) 20 | 21 | find_package(Sanitizers) 22 | 23 | enable_testing() 24 | 25 | add_executable(lwrb_test test.c ${LWRB_DIR}/lwrb/lwrb.c ${UNITY_DIR}/src/unity.c) 26 | target_include_directories(lwrb_test PRIVATE ${LWRB_DIR}/include ${UNITY_DIR}/src) 27 | 28 | add_sanitizers(lwrb_test) 29 | 30 | add_test(lwrb_test_suite lwrb_test) 31 | 32 | if(WITH_COVERAGE) 33 | setup_target_for_coverage_lcov( 34 | NAME coverage 35 | EXECUTABLE ctest 36 | EXCLUDE "${CMAKE_SOURCE_DIR}/*" "${THIRD_PARTY_DIR}/*") 37 | endif(WITH_COVERAGE) 38 | -------------------------------------------------------------------------------- /lwrb/test/README.md: -------------------------------------------------------------------------------- 1 | # Testing Suite for lwrb 2 | 3 | This directory contains an environment for writing and executing tests for the lwrb library. It supports code coverage along with different usage of sanitizers (such as ASAN and UBSAN). 4 | 5 | ## Requirements 6 | The repository must have been built recursively for retrieval of the necessary git submodules. Further more, this testing environment requires cmake. For sanitizers you need to use a compiler which has the sanitizer dependencies availble. For coverage, you also need gcov and lcov. 7 | 8 | ## Build 9 | 10 | ```bash 11 | mkdir build 12 | cd build 13 | cmake -DSANITIZE_ADDRESS=On -DSANITIZE_UNDEFINED=On -DWITH_COVERAGE=On .. 14 | make -j 15 | ``` 16 | 17 | `-DSANITIZE_ADDRESS=On -DSANITIZE_UNDEFINED=On` are optional to pass to the cmake invocation. Including is a good idea to find more potential bugs, but if you don't have sanitizer libraries in stalled in your computer you can simply discard those optional features. 18 | 19 | ## Run tests 20 | ```bash 21 | # From within the build directory created before 22 | ctest 23 | # or make test 24 | ``` 25 | 26 | ## Coverage output 27 | ```bash 28 | # From within the build directory created before 29 | # Don't use ninja when configuring cmake if you want to run this command 30 | make coverage 31 | # Assuming you have google chrome installed on a linux host machine 32 | google-chrome coverage/index.html # To see the coverage html output 33 | ``` 34 | 35 | ## Future work 36 | Run this in a CI environment such as Travis. Pass the result and coverage output as banners in the repository. 37 | -------------------------------------------------------------------------------- /lwrb/test/llvm-cov.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | exec llvm-cov gcov "$@" -------------------------------------------------------------------------------- /lwrb/test/test.c: -------------------------------------------------------------------------------- 1 | /** 2 | * @file test.c 3 | * 4 | * Unit tests for the lwrb library 5 | * 6 | * @author Tofik Sonono (tofik@sonono.me) 7 | * 8 | */ 9 | 10 | /*======= Includes ==========================================================*/ 11 | 12 | #include 13 | #include 14 | #include "lwrb/lwrb.h" 15 | #include "unity.h" 16 | 17 | /*======= Local Macro Definitions ===========================================*/ 18 | /*======= Local function prototypes =========================================*/ 19 | 20 | void basic_read_and_write(lwrb_t* buff, uint8_t* data_to_write, size_t data_size); 21 | 22 | /*======= Local variable declarations =======================================*/ 23 | /*======= Global function implementations ===================================*/ 24 | 25 | /* Requires a definition for Unity to compile */ 26 | 27 | void 28 | setUp(void) {} 29 | 30 | void 31 | tearDown(void) {} 32 | 33 | /*======= Tests ==============================================================*/ 34 | 35 | void 36 | testNullInputToInit_should_fail(void) { 37 | uint8_t ret; 38 | lwrb_t buff = {0}; 39 | uint8_t buff_data[1]; 40 | 41 | ret = lwrb_init(NULL, buff_data, sizeof(buff_data)); 42 | TEST_ASSERT_EQUAL(0, ret); 43 | 44 | ret = lwrb_init(&buff, NULL, sizeof(buff_data)); 45 | TEST_ASSERT_EQUAL(0, ret); 46 | 47 | ret = lwrb_init(&buff, buff_data, 0); 48 | TEST_ASSERT_EQUAL(0, ret); 49 | 50 | ret = lwrb_is_ready(&buff); 51 | TEST_ASSERT_EQUAL(0, ret); 52 | } 53 | 54 | void 55 | testNormalInputToInit_should_succeed(void) { 56 | uint8_t ret; 57 | lwrb_t buff = {0}; 58 | uint8_t buff_data[1]; 59 | 60 | ret = lwrb_init(&buff, buff_data, sizeof(buff_data)); 61 | TEST_ASSERT_EQUAL(1, ret); 62 | 63 | ret = lwrb_is_ready(&buff); 64 | TEST_ASSERT_EQUAL(1, ret); 65 | } 66 | 67 | void 68 | testAddElementsToQueueAndRead_should_succeed(void) { 69 | uint8_t data_to_write[] = {0, 1, 2, 3, 4, 5, 6, 7}; 70 | lwrb_t buff = {0}; 71 | basic_read_and_write(&buff, data_to_write, sizeof(data_to_write)); 72 | } 73 | 74 | void 75 | testAddElementsToQueueAndReadAndVerifyEmpty_should_succeed(void) { 76 | uint8_t data_to_write[] = {0, 1, 2, 3, 4, 5, 6, 7}; 77 | lwrb_t buff = {0}; 78 | basic_read_and_write(&buff, data_to_write, sizeof(data_to_write)); 79 | 80 | size_t n_free_bytes = lwrb_get_free(&buff); 81 | TEST_ASSERT_EQUAL(sizeof(data_to_write), n_free_bytes); 82 | } 83 | 84 | void 85 | testAddElementsToQueueAndReadTooSmallBuffer_should_fail(void) { 86 | uint8_t data_to_write[] = {0, 1, 2, 3, 4, 5, 6, 7}; 87 | lwrb_t buff = {0}; 88 | 89 | uint8_t ret; 90 | uint8_t buff_data[sizeof(data_to_write)]; 91 | 92 | ret = lwrb_init(&buff, buff_data, sizeof(buff_data)); 93 | TEST_ASSERT_EQUAL(1, ret); 94 | 95 | ret = lwrb_is_ready(&buff); 96 | TEST_ASSERT_EQUAL(1, ret); 97 | 98 | size_t n_written = lwrb_write(&buff, data_to_write, sizeof(data_to_write)); 99 | TEST_ASSERT_EQUAL(sizeof(data_to_write) - 1, n_written); 100 | } 101 | 102 | /*======= Main ===============================================================*/ 103 | 104 | int 105 | main(void) { 106 | UNITY_BEGIN(); 107 | RUN_TEST(testNullInputToInit_should_fail); 108 | RUN_TEST(testNormalInputToInit_should_succeed); 109 | RUN_TEST(testAddElementsToQueueAndRead_should_succeed); 110 | RUN_TEST(testAddElementsToQueueAndReadAndVerifyEmpty_should_succeed); 111 | RUN_TEST(testAddElementsToQueueAndReadTooSmallBuffer_should_fail); 112 | return UNITY_END(); 113 | } 114 | 115 | /*======= Local function implementations =====================================*/ 116 | 117 | void 118 | basic_read_and_write(lwrb_t* buff, uint8_t* data_to_write, size_t data_size) { 119 | uint8_t ret; 120 | size_t buffer_size = (sizeof(uint8_t) * data_size) + 1; 121 | uint8_t* buff_data = malloc(buffer_size); 122 | 123 | ret = lwrb_init(buff, buff_data, buffer_size); 124 | TEST_ASSERT_EQUAL(1, ret); 125 | 126 | ret = lwrb_is_ready(buff); 127 | TEST_ASSERT_EQUAL(1, ret); 128 | 129 | size_t n_written = lwrb_write(buff, data_to_write, data_size); 130 | TEST_ASSERT_EQUAL(data_size, n_written); 131 | 132 | size_t n_bytes_in_queue = lwrb_get_full(buff); 133 | TEST_ASSERT_EQUAL(n_written, n_bytes_in_queue); 134 | 135 | uint8_t read_buffer[data_size]; 136 | size_t n_read = lwrb_read(buff, read_buffer, n_bytes_in_queue); 137 | TEST_ASSERT_EQUAL(n_bytes_in_queue, n_read); 138 | 139 | TEST_ASSERT_EQUAL_UINT8_ARRAY(data_to_write, read_buffer, data_size); 140 | 141 | free(buff_data); 142 | } 143 | -------------------------------------------------------------------------------- /lwrb/test/test_ex.c: -------------------------------------------------------------------------------- 1 | #ifdef TEST 2 | 3 | #include "unity.h" 4 | 5 | #include "lwrb.h" 6 | 7 | #define TEST_ARRAY_SIZE 10 8 | 9 | static uint8_t lwrb_data[TEST_ARRAY_SIZE] = { 0x00 }; 10 | static uint8_t lwrb_data2[TEST_ARRAY_SIZE] = { 0x00 }; 11 | static uint8_t lwrb_result[TEST_ARRAY_SIZE]; 12 | static lwrb_t lwrb_test; 13 | static lwrb_t lwrb_test2; 14 | 15 | 16 | void setUp(void) 17 | { 18 | memset(lwrb_data, 0x00, TEST_ARRAY_SIZE); 19 | memset(lwrb_data2, 0xFF, TEST_ARRAY_SIZE); 20 | lwrb_init(&lwrb_test, lwrb_data, TEST_ARRAY_SIZE); 21 | lwrb_init(&lwrb_test2, lwrb_data2, TEST_ARRAY_SIZE); 22 | } 23 | 24 | void tearDown(void) 25 | { 26 | } 27 | 28 | 29 | void test_lwrb_ex_copy_1_linear_linear_simple(void) 30 | { 31 | // Given 32 | uint8_t input[10] = { 0x00 }; 33 | uint8_t output[TEST_ARRAY_SIZE] = { 0x00 }; 34 | size_t copied; 35 | 36 | // When 37 | //[AA][BB][xx][xx][xx][xx][xx][xx][xx][xx] 38 | // r w 39 | //[00][00][00][00][00][00][00][00][00][00] 40 | // rw 41 | input[0] = 0xAA; 42 | input[1] = 0xBB; 43 | lwrb_write(&lwrb_test, input, 2); 44 | copied = lwrb_copy(&lwrb_test2, &lwrb_test); 45 | 46 | // Then 47 | //[AA][BB][xx][xx][xx][xx][xx][xx][xx][xx] 48 | // rw 49 | //[AA][BB][00][00][00][00][00][00][00][00] 50 | // r w 51 | TEST_ASSERT_NOT_NULL(lwrb_test.buff); 52 | TEST_ASSERT_NOT_NULL(lwrb_test2.buff); 53 | TEST_ASSERT_EQUAL(copied, 2); 54 | TEST_ASSERT_EQUAL(lwrb_test.w, 2); 55 | TEST_ASSERT_EQUAL(lwrb_test.r, 2); 56 | TEST_ASSERT_EQUAL(lwrb_test2.w, 2); 57 | TEST_ASSERT_EQUAL(lwrb_test2.r, 0); 58 | 59 | TEST_ASSERT_EQUAL(lwrb_data[0], 0xAA); 60 | TEST_ASSERT_EQUAL(lwrb_data[1], 0xBB); 61 | TEST_ASSERT_EQUAL(lwrb_data2[0], 0xAA); 62 | TEST_ASSERT_EQUAL(lwrb_data2[1], 0xBB); 63 | 64 | lwrb_read(&lwrb_test2, output, TEST_ARRAY_SIZE); 65 | TEST_ASSERT_EQUAL(output[0], 0xAA); 66 | TEST_ASSERT_EQUAL(output[1], 0xBB); 67 | } 68 | 69 | 70 | void test_lwrb_ex_copy_2_linear_linear_end(void) 71 | { 72 | // Given 73 | uint8_t input[10] = { 0x00 }; 74 | uint8_t output[TEST_ARRAY_SIZE] = { 0x00 }; 75 | size_t copied; 76 | 77 | // When 78 | //[xx][AA][BB][CC][DD][EE][FF][00][11][22] 79 | // w r 80 | //[xx][00][00][00][00][00][00][00][00][00] 81 | // rw 82 | input[0] = 0xAA; 83 | input[1] = 0xBB; 84 | input[2] = 0xCC; 85 | input[3] = 0xDD; 86 | input[4] = 0xEE; 87 | input[5] = 0xFF; 88 | input[6] = 0x00; 89 | input[7] = 0x11; 90 | input[8] = 0x22; 91 | lwrb_test.r = 1; 92 | lwrb_test.w = 1; 93 | lwrb_test2.r = 1; 94 | lwrb_test2.w = 1; 95 | lwrb_write(&lwrb_test, input, 9); 96 | copied = lwrb_copy(&lwrb_test2, &lwrb_test); 97 | 98 | // Then 99 | //[xx][AA][BB][CC][DD][EE][FF][00][11][22] 100 | // rw 101 | //[xx][AA][BB][CC][DD][EE][FF][00][11][22] 102 | // w r 103 | TEST_ASSERT_NOT_NULL(lwrb_test.buff); 104 | TEST_ASSERT_NOT_NULL(lwrb_test2.buff); 105 | TEST_ASSERT_EQUAL(copied, 9); 106 | TEST_ASSERT_EQUAL(lwrb_test.w, 0); 107 | TEST_ASSERT_EQUAL(lwrb_test.r, 0); 108 | TEST_ASSERT_EQUAL(lwrb_test2.w, 0); 109 | TEST_ASSERT_EQUAL(lwrb_test2.r, 1); 110 | 111 | TEST_ASSERT_EQUAL(lwrb_data[1], 0xAA); 112 | TEST_ASSERT_EQUAL(lwrb_data[9], 0x22); 113 | TEST_ASSERT_EQUAL(lwrb_data2[1], 0xAA); 114 | TEST_ASSERT_EQUAL(lwrb_data2[9], 0x22); 115 | 116 | lwrb_read(&lwrb_test2, output, TEST_ARRAY_SIZE); 117 | TEST_ASSERT_EQUAL(output[0], 0xAA); 118 | TEST_ASSERT_EQUAL(output[8], 0x22); 119 | } 120 | 121 | 122 | void test_lwrb_ex_copy_3_linear_wrap(void) 123 | { 124 | // Given 125 | uint8_t input[10] = { 0x00 }; 126 | uint8_t output[TEST_ARRAY_SIZE] = { 0x00 }; 127 | size_t copied; 128 | 129 | // When 130 | //[AA][BB][FF][FF][FF][FF][FF][FF][FF][FF] 131 | // r w 132 | //[00][00][00][00][00][00][00][00][00][00] 133 | // rw 134 | input[0] = 0xAA; 135 | input[1] = 0xBB; 136 | lwrb_test.r = 0; 137 | lwrb_test.w = 0; 138 | lwrb_test2.r = 9; 139 | lwrb_test2.w = 9; 140 | lwrb_write(&lwrb_test, input, 2); 141 | copied = lwrb_copy(&lwrb_test2, &lwrb_test); 142 | 143 | // Then 144 | //[AA][BB][FF][FF][FF][FF][FF][FF][FF][FF] 145 | // rw 146 | //[BB][00][00][00][00][00][00][00][00][AA] 147 | // w r 148 | TEST_ASSERT_NOT_NULL(lwrb_test.buff); 149 | TEST_ASSERT_NOT_NULL(lwrb_test2.buff); 150 | TEST_ASSERT_EQUAL(copied, 2); 151 | TEST_ASSERT_EQUAL(lwrb_test.w, 2); 152 | TEST_ASSERT_EQUAL(lwrb_test.r, 2); 153 | TEST_ASSERT_EQUAL(lwrb_test2.w, 1); 154 | TEST_ASSERT_EQUAL(lwrb_test2.r, 9); 155 | 156 | TEST_ASSERT_EQUAL(lwrb_data[0], 0xAA); 157 | TEST_ASSERT_EQUAL(lwrb_data[1], 0xBB); 158 | TEST_ASSERT_EQUAL(lwrb_data2[9], 0xAA); 159 | TEST_ASSERT_EQUAL(lwrb_data2[0], 0xBB); 160 | 161 | lwrb_read(&lwrb_test2, output, TEST_ARRAY_SIZE); 162 | TEST_ASSERT_EQUAL(output[0], 0xAA); 163 | TEST_ASSERT_EQUAL(output[1], 0xBB); 164 | } 165 | 166 | 167 | void test_lwrb_ex_copy_4_wrap_wrap(void) 168 | { 169 | // Given 170 | uint8_t input[10] = { 0x00 }; 171 | uint8_t output[TEST_ARRAY_SIZE] = { 0x00 }; 172 | size_t copied; 173 | 174 | // When 175 | //[BB][00][00][00][00][00][00][00][00][AA] 176 | // w r 177 | //[00][00][00][00][00][00][00][00][00][00] 178 | // rw 179 | input[0] = 0xAA; 180 | input[1] = 0xBB; 181 | lwrb_test.r = 9; 182 | lwrb_test.w = 9; 183 | lwrb_test2.r = 9; 184 | lwrb_test2.w = 9; 185 | lwrb_write(&lwrb_test, input, 2); 186 | copied = lwrb_copy(&lwrb_test2, &lwrb_test); 187 | 188 | // Then 189 | //[BB][00][00][00][00][00][00][00][00][AA] 190 | // rw 191 | //[BB][00][00][00][00][00][00][00][00][AA] 192 | // w r 193 | TEST_ASSERT_NOT_NULL(lwrb_test.buff); 194 | TEST_ASSERT_NOT_NULL(lwrb_test2.buff); 195 | TEST_ASSERT_EQUAL(copied, 2); 196 | TEST_ASSERT_EQUAL(lwrb_test.w, 1); 197 | TEST_ASSERT_EQUAL(lwrb_test.r, 1); 198 | TEST_ASSERT_EQUAL(lwrb_test2.w, 1); 199 | TEST_ASSERT_EQUAL(lwrb_test2.r, 9); 200 | 201 | TEST_ASSERT_EQUAL(lwrb_data[9], 0xAA); 202 | TEST_ASSERT_EQUAL(lwrb_data[0], 0xBB); 203 | TEST_ASSERT_EQUAL(lwrb_data2[9], 0xAA); 204 | TEST_ASSERT_EQUAL(lwrb_data2[0], 0xBB); 205 | 206 | lwrb_read(&lwrb_test2, output, TEST_ARRAY_SIZE); 207 | TEST_ASSERT_EQUAL(output[0], 0xAA); 208 | TEST_ASSERT_EQUAL(output[1], 0xBB); 209 | } 210 | 211 | 212 | void test_lwrb_ex_overwrite_1_linear_under_free(void) 213 | { 214 | // Given 215 | uint8_t input[TEST_ARRAY_SIZE] = { 0xAA, 0xBB }; 216 | uint8_t output[TEST_ARRAY_SIZE] = { 0x00 }; 217 | 218 | // When 219 | lwrb_overwrite(&lwrb_test, input, 2); 220 | 221 | // Then 222 | TEST_ASSERT_NOT_NULL(lwrb_test.buff); 223 | TEST_ASSERT_EQUAL(lwrb_test.w, 2); 224 | TEST_ASSERT_EQUAL(lwrb_test.r, 0); 225 | 226 | TEST_ASSERT_EQUAL(lwrb_data[0], 0xAA); 227 | TEST_ASSERT_EQUAL(lwrb_data[1], 0xBB); 228 | 229 | lwrb_read(&lwrb_test, output, TEST_ARRAY_SIZE); 230 | TEST_ASSERT_EQUAL(output[0], 0xAA); 231 | TEST_ASSERT_EQUAL(output[1], 0xBB); 232 | } 233 | 234 | void test_lwrb_ex_overwrite_2_wrap_under_free(void) 235 | { 236 | 237 | // Given 238 | uint8_t input[TEST_ARRAY_SIZE] = { 0xAA, 0xBB }; 239 | uint8_t output[TEST_ARRAY_SIZE] = { 0x00 }; 240 | 241 | // When 242 | lwrb_test.r = 9; // TODO: Try at 8 243 | lwrb_test.w = 9; 244 | lwrb_overwrite(&lwrb_test, input, 2); 245 | 246 | 247 | // Then 248 | TEST_ASSERT_NOT_NULL(lwrb_test.buff); 249 | TEST_ASSERT_EQUAL(lwrb_test.w, 1); 250 | TEST_ASSERT_EQUAL(lwrb_test.r, 9); 251 | 252 | TEST_ASSERT_EQUAL(lwrb_data[9], 0xAA); 253 | TEST_ASSERT_EQUAL(lwrb_data[0], 0xBB); 254 | 255 | lwrb_read(&lwrb_test, output, TEST_ARRAY_SIZE); 256 | TEST_ASSERT_EQUAL(output[0], 0xAA); 257 | TEST_ASSERT_EQUAL(output[1], 0xBB); 258 | } 259 | 260 | void test_lwrb_ex_overwrite_3_linear_over_free(void) 261 | { 262 | // Given 263 | uint8_t input[TEST_ARRAY_SIZE] = { 0xAA, 0xBB }; 264 | uint8_t output[TEST_ARRAY_SIZE] = { 0x00 }; 265 | 266 | // When 267 | lwrb_test.r = 4; 268 | lwrb_test.w = 3; 269 | lwrb_overwrite(&lwrb_test, input, 2); 270 | 271 | // Then 272 | TEST_ASSERT_NOT_NULL(lwrb_test.buff); 273 | TEST_ASSERT_EQUAL(lwrb_test.w, 5); 274 | TEST_ASSERT_EQUAL(lwrb_test.r, 6); 275 | 276 | TEST_ASSERT_EQUAL(lwrb_data[2], 0x00); 277 | TEST_ASSERT_EQUAL(lwrb_data[3], 0xAA); 278 | TEST_ASSERT_EQUAL(lwrb_data[4], 0xBB); 279 | TEST_ASSERT_EQUAL(lwrb_data[5], 0x00); 280 | 281 | lwrb_read(&lwrb_test, output, TEST_ARRAY_SIZE); 282 | TEST_ASSERT_EQUAL(output[7], 0xAA); 283 | TEST_ASSERT_EQUAL(output[8], 0xBB); 284 | } 285 | 286 | 287 | void test_lwrb_ex_overwrite_4_linear_over_wrap(void) 288 | { 289 | // Given 290 | //[00][00][00][00][00][00][00][00][00][00] 291 | // rw 292 | uint8_t input[TEST_ARRAY_SIZE] = { 0xAA, 0xBB }; 293 | uint8_t output[TEST_ARRAY_SIZE] = { 0x00 }; 294 | 295 | // When 296 | //[xx][xx][xx][xx][xx][xx][xx][xx][xx][00] 297 | // r w 298 | lwrb_test.r = 0; 299 | lwrb_test.w = 9; 300 | lwrb_overwrite(&lwrb_test, input, 2); 301 | 302 | // Then 303 | //[BB][xx][xx][xx][xx][xx][xx][xx][xx][AA] 304 | // w r 305 | TEST_ASSERT_NOT_NULL(lwrb_test.buff); 306 | TEST_ASSERT_EQUAL(lwrb_test.w, 1); 307 | TEST_ASSERT_EQUAL(lwrb_test.r, 2); 308 | 309 | TEST_ASSERT_EQUAL(lwrb_data[8], 0x00); 310 | TEST_ASSERT_EQUAL(lwrb_data[9], 0xAA); 311 | TEST_ASSERT_EQUAL(lwrb_data[0], 0xBB); 312 | TEST_ASSERT_EQUAL(lwrb_data[1], 0x00); 313 | 314 | lwrb_read(&lwrb_test, output, TEST_ARRAY_SIZE); 315 | TEST_ASSERT_EQUAL(output[7], 0xAA); 316 | TEST_ASSERT_EQUAL(output[8], 0xBB); 317 | } 318 | 319 | 320 | void test_lwrb_ex_overwrite_5_oversize_from_middle(void) 321 | { 322 | // Given 323 | //[00][00][00][00][00][00][00][00][00][00] 324 | // rw 325 | uint8_t input[100] = { 0x00 }; 326 | input[91] = 0xAA; 327 | input[92] = 0xBB; 328 | input[98] = 0xEE; 329 | input[99] = 0xFF; 330 | uint8_t output[TEST_ARRAY_SIZE] = { 0x00 }; 331 | 332 | // When 333 | //[xx][xx][xx][xx][xx][xx][xx][xx][xx][00] 334 | // r w 335 | lwrb_test.r = 5; 336 | lwrb_test.w = 6; 337 | lwrb_overwrite(&lwrb_test, input, 100); 338 | 339 | // Then 340 | //[xx][xx][xx][EE][FF][xx][AA][BB][xx][xx] 341 | // w r 342 | TEST_ASSERT_NOT_NULL(lwrb_test.buff); 343 | TEST_ASSERT_EQUAL(lwrb_test.w, 5); 344 | TEST_ASSERT_EQUAL(lwrb_test.r, 6); 345 | 346 | TEST_ASSERT_EQUAL(lwrb_data[6], 0xAA); 347 | TEST_ASSERT_EQUAL(lwrb_data[7], 0xBB); 348 | TEST_ASSERT_EQUAL(lwrb_data[3], 0xEE); 349 | TEST_ASSERT_EQUAL(lwrb_data[4], 0xFF); 350 | 351 | lwrb_read(&lwrb_test, output, TEST_ARRAY_SIZE); 352 | TEST_ASSERT_EQUAL(output[0], 0xAA); 353 | TEST_ASSERT_EQUAL(output[1], 0xBB); 354 | TEST_ASSERT_EQUAL(output[7], 0xEE); 355 | TEST_ASSERT_EQUAL(output[8], 0xFF); 356 | } 357 | 358 | 359 | void test_lwrb_ex_overwrite_6_oversize_from_zero(void) 360 | { 361 | // Given 362 | //[00][00][00][00][00][00][00][00][00][00] 363 | // rw 364 | uint8_t input[100] = { 0x00 }; 365 | input[91] = 0xAA; 366 | input[92] = 0xBB; 367 | input[98] = 0xEE; 368 | input[99] = 0xFF; 369 | uint8_t output[TEST_ARRAY_SIZE] = { 0x00 }; 370 | 371 | // When 372 | //[xx][xx][xx][xx][xx][xx][xx][xx][xx][xx] 373 | // rw 374 | lwrb_test.r = 0; 375 | lwrb_test.w = 0; 376 | lwrb_overwrite(&lwrb_test, input, 100); 377 | 378 | // Then 379 | //[00][AA][BB][00][00][00][00][00][EE][FF] 380 | // w r 381 | TEST_ASSERT_NOT_NULL(lwrb_test.buff); 382 | TEST_ASSERT_EQUAL(lwrb_test.w, 9); 383 | TEST_ASSERT_EQUAL(lwrb_test.r, 0); 384 | 385 | TEST_ASSERT_EQUAL(lwrb_data[0], 0xAA); 386 | TEST_ASSERT_EQUAL(lwrb_data[1], 0xBB); 387 | TEST_ASSERT_EQUAL(lwrb_data[7], 0xEE); 388 | TEST_ASSERT_EQUAL(lwrb_data[8], 0xFF); 389 | 390 | lwrb_read(&lwrb_test, output, TEST_ARRAY_SIZE); 391 | TEST_ASSERT_EQUAL(output[0], 0xAA); 392 | TEST_ASSERT_EQUAL(output[1], 0xBB); 393 | TEST_ASSERT_EQUAL(output[7], 0xEE); 394 | TEST_ASSERT_EQUAL(output[8], 0xFF); 395 | } 396 | 397 | 398 | #endif // TEST 399 | --------------------------------------------------------------------------------