SolidSyslog coverage report
Current view: top level - Core/Source - SolidSyslogBlockSequence.c (source / functions) Coverage Total Hit
Test: SolidSyslog Lines: 100.0 % 276 276
Test Date: 2026-11-12 23:14:35 Functions: 100.0 % 52 52
Branches: 99.1 % 106 105

             Branch data     Line data    Source code
       1                 :             : /* SPDX-FileCopyrightText: Copyright 2026 Cozens Software Solutions Limited
       2                 :             :  * SPDX-License-Identifier: PolyForm-Noncommercial-1.0.0 OR LicenseRef-PolyForm-Internal-Use-1.0.0 OR LicenseRef-COSOSO-Commercial
       3                 :             :  */
       4                 :             : 
       5                 :             : #include "SolidSyslogBlockSequencePrivate.h"
       6                 :             : 
       7                 :             : #include "SolidSyslogBlockDevice.h"
       8                 :             : #include "SolidSyslogBlockStore.h"
       9                 :             : #include "SolidSyslogBlockStoreErrors.h"
      10                 :             : #include "SolidSyslogBlockStorePrivate.h"
      11                 :             : #include "SolidSyslogPrival.h"
      12                 :             : #include "SolidSyslogStoreCategories.h"
      13                 :             : 
      14                 :             : #include <stdbool.h>
      15                 :             : #include <stddef.h>
      16                 :             : #include <stdint.h>
      17                 :             : 
      18                 :             : struct SolidSyslogBlockDevice;
      19                 :             : 
      20                 :             : enum
      21                 :             : {
      22                 :             :     MIN_MAX_BLOCKS = 2U,
      23                 :             :     /* Two short of the sequence space: a failed eviction leaves one block over
      24                 :             :      * the ceiling, and a resume still needs an absent number to find the run. */
      25                 :             :     MAX_MAX_BLOCKS = BLOCK_SEQUENCE_MODULUS - 2U
      26                 :             : };
      27                 :             : 
      28                 :       29282 : static inline uint8_t BlockSequence_NextSequence(uint8_t current)
      29                 :             : {
      30                 :       29282 :     return (uint8_t) ((current + 1U) % BLOCK_SEQUENCE_MODULUS);
      31                 :             : }
      32                 :             : 
      33                 :       33670 : static inline size_t BlockSequence_BlockCount(const struct SolidSyslogBlockSequence* blockSequence)
      34                 :             : {
      35                 :       33670 :     return (size_t) ((blockSequence->WriteSequence - blockSequence->OldestSequence + BLOCK_SEQUENCE_MODULUS) %
      36                 :       33670 :                      BLOCK_SEQUENCE_MODULUS) +
      37                 :             :            1U;
      38                 :             : }
      39                 :             : 
      40                 :             : // NOLINTNEXTLINE(bugprone-easily-swappable-parameters) -- value, min, max have distinct semantics
      41                 :       19219 : static inline size_t BlockSequence_ClampToRange(size_t value, size_t min, size_t max)
      42                 :             : {
      43                 :       19219 :     size_t result = value;
      44                 :             : 
      45         [ +  + ]:       19219 :     if (result < min)
      46                 :             :     {
      47                 :           9 :         result = min;
      48                 :             :     }
      49                 :             : 
      50         [ +  + ]:       19219 :     if (result > max)
      51                 :             :     {
      52                 :          27 :         result = max;
      53                 :             :     }
      54                 :             : 
      55                 :       19219 :     return result;
      56                 :             : }
      57                 :             : 
      58                 :             : static void BlockSequence_NullThresholdCrossed(void* context);
      59                 :             : 
      60                 :       19219 : void SolidSyslogBlockSequence_Initialise(
      61                 :             :     struct SolidSyslogBlockSequence* blockSequence,
      62                 :             :     const struct SolidSyslogBlockSequenceConfig* config
      63                 :             : )
      64                 :             : {
      65                 :       19219 :     blockSequence->BlockDevice = config->BlockDevice;
      66                 :       19219 :     blockSequence->MaxBlockSize = config->MaxBlockSize;
      67                 :       19219 :     blockSequence->MaxBlocks = BlockSequence_ClampToRange(config->MaxBlocks, MIN_MAX_BLOCKS, MAX_MAX_BLOCKS);
      68                 :       19219 :     blockSequence->GetDiscardPolicy =
      69         [ +  + ]:       19219 :         (config->GetDiscardPolicy != NULL) ? config->GetDiscardPolicy : SolidSyslogDiscardPolicy_Default;
      70                 :       19219 :     blockSequence->DiscardPolicyContext = config->DiscardPolicyContext;
      71                 :       19219 :     blockSequence->GetCapacityThreshold = config->GetCapacityThreshold;
      72                 :       19219 :     blockSequence->OnThresholdCrossed =
      73         [ +  + ]:       19219 :         (config->OnThresholdCrossed != NULL) ? config->OnThresholdCrossed : BlockSequence_NullThresholdCrossed;
      74                 :       19219 :     blockSequence->ThresholdContext = config->ThresholdContext;
      75                 :       19219 :     blockSequence->AtCapacity = false;
      76                 :       19219 :     blockSequence->ThresholdCrossed = false;
      77                 :       19219 :     blockSequence->OldestSequence = 0;
      78                 :       19219 :     blockSequence->ReadSequence = 0;
      79                 :       19219 :     blockSequence->WriteSequence = 0;
      80                 :       19219 :     blockSequence->ReadCursor = 0;
      81                 :       19219 :     blockSequence->ReadBlockSize = 0;
      82                 :       19219 :     blockSequence->ReadBlockSizeKnown = false;
      83                 :       19219 :     blockSequence->WritePosition = 0;
      84                 :       19219 :     blockSequence->WriteBlockCorrupt = false;
      85                 :       19219 :     blockSequence->DeviceFailed = false;
      86                 :       19219 :     blockSequence->EvictedUnsent = false;
      87                 :       19219 :     blockSequence->FailedOperation = SOLIDSYSLOG_BLOCK_STORE_ERROR_MAX;
      88                 :             : 
      89         [ +  + ]:     1941119 :     for (size_t seq = 0; seq < BLOCK_SEQUENCE_MODULUS; seq++)
      90                 :             :     {
      91                 :     1921900 :         blockSequence->BlockPresent[seq] = false;
      92                 :             :     }
      93                 :       19219 : }
      94                 :             : 
      95                 :             : /* A threshold with no callback is still raised as an event. */
      96                 :           1 : static void BlockSequence_NullThresholdCrossed(void* context)
      97                 :             : {
      98                 :             :     (void) context;
      99                 :           1 : }
     100                 :             : 
     101                 :       19219 : void SolidSyslogBlockSequence_Cleanup(struct SolidSyslogBlockSequence* blockSequence)
     102                 :             : {
     103                 :             :     /* No owned resources to release. The BlockDevice pointer is caller-owned
     104                 :             :      * (integrator-supplied via BlockSequenceConfig.BlockDevice); the next
     105                 :             :      * SolidSyslogBlockSequence_Initialise overwrites every field, so clearing here would be churn. */
     106                 :             :     (void) blockSequence;
     107                 :       19219 : }
     108                 :             : 
     109                 :             : static bool BlockSequence_ScanForExistingBlocks(struct SolidSyslogBlockSequence* blockSequence);
     110                 :             : static inline bool BlockSequence_AcquireBlock(struct SolidSyslogBlockSequence* blockSequence, size_t blockIndex);
     111                 :             : static inline bool BlockSequence_DisposeBlock(struct SolidSyslogBlockSequence* blockSequence, size_t blockIndex);
     112                 :             : static inline uint8_t BlockSequence_NextPresentSequence(
     113                 :             :     const struct SolidSyslogBlockSequence* blockSequence,
     114                 :             :     uint8_t current
     115                 :             : );
     116                 :             : static inline void BlockSequence_NoteDeviceFailure(
     117                 :             :     struct SolidSyslogBlockSequence* blockSequence,
     118                 :             :     enum SolidSyslogBlockStoreErrors failedOperation
     119                 :             : );
     120                 :             : static inline void BlockSequence_NotifyThresholdCrossed(struct SolidSyslogBlockSequence* blockSequence);
     121                 :             : 
     122                 :       19209 : bool SolidSyslogBlockSequence_Open(struct SolidSyslogBlockSequence* blockSequence)
     123                 :             : {
     124                 :       19209 :     bool foundExistingBlocks = BlockSequence_ScanForExistingBlocks(blockSequence);
     125                 :       19209 :     bool ready = false;
     126                 :             : 
     127         [ +  + ]:       19209 :     if (foundExistingBlocks)
     128                 :             :     {
     129                 :       12288 :         size_t writePosition = 0U;
     130                 :       12288 :         ready = SolidSyslogBlockDevice_Size(blockSequence->BlockDevice, blockSequence->WriteSequence, &writePosition);
     131                 :       12288 :         blockSequence->WritePosition = writePosition;
     132                 :             : 
     133         [ +  + ]:       12288 :         if (!ready)
     134                 :             :         {
     135                 :         320 :             BlockSequence_NoteDeviceFailure(blockSequence, SOLIDSYSLOG_BLOCK_STORE_ERROR_SIZE_FAILED);
     136                 :             :         }
     137                 :             :     }
     138                 :             :     else
     139                 :             :     {
     140                 :        6921 :         ready = BlockSequence_AcquireBlock(blockSequence, 0);
     141                 :             : 
     142         [ +  + ]:        6921 :         if (!ready)
     143                 :             :         {
     144                 :           4 :             BlockSequence_NoteDeviceFailure(blockSequence, SOLIDSYSLOG_BLOCK_STORE_ERROR_OPEN_FAILED);
     145                 :             :         }
     146                 :             :     }
     147                 :             : 
     148         [ +  + ]:       19209 :     if (ready)
     149                 :             :     {
     150                 :       18885 :         BlockSequence_NotifyThresholdCrossed(blockSequence); /* fire on resume if usage already at-or-above threshold */
     151                 :             :     }
     152                 :             : 
     153                 :       19209 :     return ready;
     154                 :             : }
     155                 :             : 
     156                 :             : /* The map follows the device: a block it acquired is present... */
     157                 :       22244 : static inline bool BlockSequence_AcquireBlock(struct SolidSyslogBlockSequence* blockSequence, size_t blockIndex)
     158                 :             : {
     159                 :       22244 :     bool acquired = SolidSyslogBlockDevice_Acquire(blockSequence->BlockDevice, blockIndex);
     160                 :             : 
     161         [ +  + ]:       22244 :     if (acquired)
     162                 :             :     {
     163                 :       22237 :         blockSequence->BlockPresent[blockIndex] = true;
     164                 :             :     }
     165                 :             : 
     166                 :       22244 :     return acquired;
     167                 :             : }
     168                 :             : 
     169                 :         443 : static inline void BlockSequence_NoteDeviceFailure(
     170                 :             :     struct SolidSyslogBlockSequence* blockSequence,
     171                 :             :     enum SolidSyslogBlockStoreErrors failedOperation
     172                 :             : )
     173                 :             : {
     174         [ +  + ]:         443 :     if (!blockSequence->DeviceFailed)
     175                 :             :     {
     176                 :         442 :         blockSequence->DeviceFailed = true;
     177                 :         442 :         blockSequence->FailedOperation = failedOperation;
     178                 :             :     }
     179                 :         443 : }
     180                 :             : 
     181                 :             : /* A run of absent sequence numbers: where it starts and how many it spans. */
     182                 :             : struct Gap
     183                 :             : {
     184                 :             :     int Start;
     185                 :             :     int Length;
     186                 :             : };
     187                 :             : 
     188                 :             : static inline int BlockSequence_CircularPrev(int index);
     189                 :             : static inline int BlockSequence_CircularNext(int index);
     190                 :             : static int BlockSequence_ScanForBlockPresence(struct SolidSyslogBlockSequence* blockSequence);
     191                 :             : static void BlockSequence_LocateRunBoundaries(
     192                 :             :     const struct SolidSyslogBlockSequence* blockSequence,
     193                 :             :     int lastPresent,
     194                 :             :     int* oldest, // NOLINT(bugprone-easily-swappable-parameters) -- oldest / newest are positional run endpoints
     195                 :             :     int* newest
     196                 :             : );
     197                 :             : 
     198                 :             : static inline void BlockSequence_StartReadingBlock(
     199                 :             :     struct SolidSyslogBlockSequence* blockSequence,
     200                 :             :     uint8_t readSequence
     201                 :             : );
     202                 :             : 
     203                 :             : /* The on-disk block set is one contiguous run in the circular sequence space
     204                 :             :  * [0, BLOCK_SEQUENCE_MODULUS), which after enough rotations straddles the
     205                 :             :  * 99 -> 00 boundary (e.g. {98, 99, 0, 1}). A block missing from inside the
     206                 :             :  * run - a filesystem check removed it - splits the set with a second, smaller
     207                 :             :  * gap. So the run is read as the complement of the largest gap: oldest sits
     208                 :             :  * one past its end, write one before its start, and the hole stays inside the
     209                 :             :  * run, counting towards the block ceiling exactly as the block it replaces
     210                 :             :  * would have. That holds while the hole is shorter than the free gap: with
     211                 :             :  * the store at its ceiling the free gap is two slots, and a hole of two or
     212                 :             :  * more consecutive blocks is then read as the gap instead. */
     213                 :       19209 : static bool BlockSequence_ScanForExistingBlocks(struct SolidSyslogBlockSequence* blockSequence)
     214                 :             : {
     215                 :       19209 :     int lastPresent = BlockSequence_ScanForBlockPresence(blockSequence);
     216                 :       19209 :     bool foundAny = lastPresent >= 0;
     217                 :             : 
     218         [ +  + ]:       19209 :     if (foundAny)
     219                 :             :     {
     220                 :       12288 :         int oldest = 0;
     221                 :       12288 :         int newest = BLOCK_SEQUENCE_MODULUS - 1;
     222                 :       12288 :         BlockSequence_LocateRunBoundaries(blockSequence, lastPresent, &oldest, &newest);
     223                 :             : 
     224                 :       12288 :         blockSequence->OldestSequence = (uint8_t) oldest;
     225                 :       12288 :         BlockSequence_StartReadingBlock(blockSequence, (uint8_t) oldest);
     226                 :       12288 :         blockSequence->WriteSequence = (uint8_t) newest;
     227                 :             :     }
     228                 :             : 
     229                 :       19209 :     return foundAny;
     230                 :             : }
     231                 :             : 
     232                 :             : /* The cursor goes to the start of the block, and a size cached for the block
     233                 :             :  * it leaves is forgotten. */
     234                 :       26219 : static inline void BlockSequence_StartReadingBlock(struct SolidSyslogBlockSequence* blockSequence, uint8_t readSequence)
     235                 :             : {
     236                 :       26219 :     blockSequence->ReadSequence = readSequence;
     237                 :       26219 :     blockSequence->ReadCursor = 0;
     238                 :       26219 :     blockSequence->ReadBlockSizeKnown = false;
     239                 :       26219 : }
     240                 :             : 
     241                 :             : /* Fills the presence map, and says which sequence number holds the last
     242                 :             :  * present block - or -1 when there is none. */
     243                 :       19209 : static int BlockSequence_ScanForBlockPresence(struct SolidSyslogBlockSequence* blockSequence)
     244                 :             : {
     245                 :       19209 :     int lastPresent = -1;
     246                 :             : 
     247         [ +  + ]:     1940109 :     for (int seq = 0; seq < BLOCK_SEQUENCE_MODULUS; seq++)
     248                 :             :     {
     249                 :     1920900 :         blockSequence->BlockPresent[seq] = SolidSyslogBlockDevice_Exists(blockSequence->BlockDevice, (size_t) seq);
     250                 :             : 
     251         [ +  + ]:     1920900 :         if (blockSequence->BlockPresent[seq])
     252                 :             :         {
     253                 :       19810 :             lastPresent = seq;
     254                 :             :         }
     255                 :             :     }
     256                 :             : 
     257                 :       19209 :     return lastPresent;
     258                 :             : }
     259                 :             : 
     260                 :             : /* Walks once round the sequence space from just after a present block, so
     261                 :             :  * every gap is met whole, and keeps the largest. Nothing changes when every
     262                 :             :  * block is present: maxBlocks is clamped to BLOCK_SEQUENCE_MODULUS - 2, so
     263                 :             :  * that cannot arise from the library's own rotation, and the caller's
     264                 :             :  * defaults stand. */
     265                 :       12288 : static void BlockSequence_LocateRunBoundaries(
     266                 :             :     const struct SolidSyslogBlockSequence* blockSequence,
     267                 :             :     int lastPresent,
     268                 :             :     int* oldest, // NOLINT(bugprone-easily-swappable-parameters) -- oldest / newest are positional run endpoints
     269                 :             :     int* newest
     270                 :             : )
     271                 :             : {
     272                 :       12288 :     struct Gap largest = {0, 0};
     273                 :       12288 :     struct Gap current = {0, 0};
     274                 :       12288 :     int index = lastPresent;
     275                 :             : 
     276         [ +  + ]:     1241088 :     for (int step = 0; step < BLOCK_SEQUENCE_MODULUS; step++)
     277                 :             :     {
     278                 :     1228800 :         index = BlockSequence_CircularNext(index);
     279                 :             : 
     280         [ +  + ]:     1228800 :         if (blockSequence->BlockPresent[index])
     281                 :             :         {
     282                 :       19810 :             current.Length = 0;
     283                 :             :         }
     284                 :             :         else
     285                 :             :         {
     286         [ +  + ]:     1208990 :             if (current.Length == 0)
     287                 :             :             {
     288                 :       12316 :                 current.Start = index;
     289                 :             :             }
     290                 :             : 
     291                 :     1208990 :             current.Length++;
     292                 :             : 
     293         [ +  + ]:     1208990 :             if (current.Length > largest.Length)
     294                 :             :             {
     295                 :     1208961 :                 largest = current;
     296                 :             :             }
     297                 :             :         }
     298                 :             :     }
     299                 :             : 
     300         [ +  + ]:       12288 :     if (largest.Length > 0)
     301                 :             :     {
     302                 :       12287 :         *oldest = (largest.Start + largest.Length) % BLOCK_SEQUENCE_MODULUS;
     303                 :       12287 :         *newest = BlockSequence_CircularPrev(largest.Start);
     304                 :             :     }
     305                 :       12288 : }
     306                 :             : 
     307                 :     1228800 : static inline int BlockSequence_CircularNext(int index)
     308                 :             : {
     309                 :     1228800 :     return (index + 1) % BLOCK_SEQUENCE_MODULUS;
     310                 :             : }
     311                 :             : 
     312                 :       12287 : static inline int BlockSequence_CircularPrev(int index)
     313                 :             : {
     314                 :       12287 :     return (index + BLOCK_SEQUENCE_MODULUS - 1) % BLOCK_SEQUENCE_MODULUS;
     315                 :             : }
     316                 :             : 
     317                 :             : static inline bool BlockSequence_ThresholdEnabled(const struct SolidSyslogBlockSequence* blockSequence);
     318                 :             : static inline bool BlockSequence_IsAboveThreshold(
     319                 :             :     const struct SolidSyslogBlockSequence* blockSequence,
     320                 :             :     size_t threshold
     321                 :             : );
     322                 :             : 
     323                 :      293452 : static inline void BlockSequence_NotifyThresholdCrossed(struct SolidSyslogBlockSequence* blockSequence)
     324                 :             : {
     325         [ +  + ]:      293452 :     if (BlockSequence_ThresholdEnabled(blockSequence))
     326                 :             :     {
     327                 :        3035 :         size_t threshold = blockSequence->GetCapacityThreshold(blockSequence->ThresholdContext);
     328                 :             : 
     329         [ +  + ]:        3035 :         if (!BlockSequence_IsAboveThreshold(blockSequence, threshold))
     330                 :             :         {
     331                 :        1579 :             blockSequence->ThresholdCrossed = false;
     332                 :             :         }
     333         [ +  + ]:        1456 :         else if (!blockSequence->ThresholdCrossed)
     334                 :             :         {
     335                 :          52 :             blockSequence->ThresholdCrossed = true;
     336                 :          52 :             BlockStore_Report(
     337                 :             :                 SOLIDSYSLOG_SEVERITY_WARNING,
     338                 :             :                 SOLIDSYSLOG_CAT_STORE_THRESHOLD_CROSSED,
     339                 :             :                 SOLIDSYSLOG_BLOCK_STORE_ERROR_THRESHOLD_CROSSED
     340                 :             :             );
     341                 :          52 :             blockSequence->OnThresholdCrossed(blockSequence->ThresholdContext);
     342                 :             :         }
     343                 :             :         else
     344                 :             :         {
     345                 :             :             /* still above threshold and already notified - no edge to report */
     346                 :             :         }
     347                 :             :     }
     348                 :      293452 : }
     349                 :             : 
     350                 :      293452 : static inline bool BlockSequence_ThresholdEnabled(const struct SolidSyslogBlockSequence* blockSequence)
     351                 :             : {
     352                 :      293452 :     return blockSequence->GetCapacityThreshold != NULL;
     353                 :             : }
     354                 :             : 
     355                 :        3035 : static inline bool BlockSequence_IsAboveThreshold(
     356                 :             :     const struct SolidSyslogBlockSequence* blockSequence,
     357                 :             :     size_t threshold
     358                 :             : )
     359                 :             : {
     360   [ +  +  +  + ]:        3035 :     return (threshold != 0U) && (SolidSyslogBlockSequence_UsedBytes(blockSequence) >= threshold);
     361                 :             : }
     362                 :             : 
     363                 :             : static inline bool BlockSequence_BlockIsFull(const struct SolidSyslogBlockSequence* blockSequence, size_t recordSize);
     364                 :             : static inline bool BlockSequence_StoreIsFull(const struct SolidSyslogBlockSequence* blockSequence);
     365                 :             : static inline enum SolidSyslogDiscardPolicy BlockSequence_DiscardPolicy(
     366                 :             :     const struct SolidSyslogBlockSequence* blockSequence
     367                 :             : );
     368                 :             : static bool BlockSequence_RotateToNextBlock(struct SolidSyslogBlockSequence* blockSequence, bool* readBlockChanged);
     369                 :             : 
     370                 :      261515 : bool SolidSyslogBlockSequence_PrepareForWrite(
     371                 :             :     struct SolidSyslogBlockSequence* blockSequence,
     372                 :             :     size_t recordSize,
     373                 :             :     bool* readBlockChanged
     374                 :             : )
     375                 :             : {
     376                 :      261515 :     bool blockFull = BlockSequence_BlockIsFull(blockSequence, recordSize);
     377                 :      261515 :     bool spaceAvailable = true;
     378                 :      261515 :     *readBlockChanged = false;
     379                 :             : 
     380   [ +  +  +  + ]:      261515 :     if (blockFull && BlockSequence_StoreIsFull(blockSequence))
     381                 :             :     {
     382                 :             :         /* Fixes UsedBytes at total while the store is full and refusing, so a
     383                 :             :          * threshold at 100% fires on the refusal even with slack in the blocks.
     384                 :             :          * Cleared once a block drains or a record is accepted. */
     385                 :         253 :         blockSequence->AtCapacity = true;
     386                 :         253 :         BlockSequence_NotifyThresholdCrossed(blockSequence); /* ahead of the refusal the store reports next */
     387                 :         253 :         spaceAvailable = false;
     388                 :             :     }
     389         [ +  + ]:      261262 :     else if (blockFull)
     390                 :             :     {
     391                 :       15325 :         spaceAvailable = BlockSequence_RotateToNextBlock(blockSequence, readBlockChanged);
     392                 :             :     }
     393                 :             :     else
     394                 :             :     {
     395                 :             :         /* current block has room - leave spaceAvailable=true */
     396                 :             :     }
     397                 :             : 
     398                 :      261515 :     return spaceAvailable;
     399                 :             : }
     400                 :             : 
     401                 :      261515 : static inline bool BlockSequence_BlockIsFull(const struct SolidSyslogBlockSequence* blockSequence, size_t recordSize)
     402                 :             : {
     403         [ +  + ]:      521728 :     return (blockSequence->WriteBlockCorrupt) ||
     404         [ +  + ]:      260213 :            ((blockSequence->WritePosition + recordSize) > blockSequence->MaxBlockSize);
     405                 :             : }
     406                 :             : 
     407                 :             : /* The policy is asked only once the store is at its block ceiling, so an
     408                 :             :  * integrator's function answers at the moment it has to decide, and nothing
     409                 :             :  * earlier. */
     410                 :       15578 : static inline bool BlockSequence_StoreIsFull(const struct SolidSyslogBlockSequence* blockSequence)
     411                 :             : {
     412   [ +  +  +  + ]:       15899 :     return (BlockSequence_BlockCount(blockSequence) >= blockSequence->MaxBlocks) &&
     413                 :         321 :            (BlockSequence_DiscardPolicy(blockSequence) != SOLIDSYSLOG_DISCARD_POLICY_OLDEST);
     414                 :             : }
     415                 :             : 
     416                 :         321 : static inline enum SolidSyslogDiscardPolicy BlockSequence_DiscardPolicy(
     417                 :             :     const struct SolidSyslogBlockSequence* blockSequence
     418                 :             : )
     419                 :             : {
     420                 :         321 :     return blockSequence->GetDiscardPolicy(blockSequence->DiscardPolicyContext);
     421                 :             : }
     422                 :             : 
     423                 :             : static bool BlockSequence_DiscardOldestBlock(struct SolidSyslogBlockSequence* blockSequence);
     424                 :             : static void BlockSequence_ResetReadToOldest(struct SolidSyslogBlockSequence* blockSequence);
     425                 :             : 
     426                 :             : static inline void BlockSequence_AdvanceWriteToNewBlock(
     427                 :             :     struct SolidSyslogBlockSequence* blockSequence,
     428                 :             :     uint8_t nextSequence
     429                 :             : );
     430                 :             : static inline bool BlockSequence_ExceedsMaxBlocks(const struct SolidSyslogBlockSequence* blockSequence);
     431                 :             : static inline bool BlockSequence_AcquireEmptyBlock(struct SolidSyslogBlockSequence* blockSequence, size_t blockIndex);
     432                 :             : 
     433                 :       15325 : static bool BlockSequence_RotateToNextBlock(struct SolidSyslogBlockSequence* blockSequence, bool* readBlockChanged)
     434                 :             : {
     435                 :       15325 :     uint8_t nextSequence = BlockSequence_NextSequence(blockSequence->WriteSequence);
     436                 :       15325 :     bool acquired = BlockSequence_AcquireEmptyBlock(blockSequence, nextSequence);
     437                 :             : 
     438                 :       15325 :     *readBlockChanged = false;
     439                 :             : 
     440         [ +  + ]:       15325 :     if (acquired)
     441                 :             :     {
     442                 :       15320 :         BlockSequence_AdvanceWriteToNewBlock(blockSequence, nextSequence);
     443                 :       15320 :         blockSequence->EvictedUnsent = false;
     444                 :             : 
     445                 :             :         /* Sealing the prior write block can re-arm dispose-on-empty: drain that
     446                 :             :          * fully MarkSent the block before rotation could not fire here because
     447                 :             :          * BlockSequence_IsReadBlockActiveWrite was true at MarkSent time. It is
     448                 :             :          * re-evaluated before any eviction, so a block with nothing left to send
     449                 :             :          * is disposed rather than evicted, and an eviction always costs records. */
     450                 :       15320 :         SolidSyslogBlockSequence_DisposeReadBlockIfDrained(blockSequence, readBlockChanged);
     451                 :             : 
     452                 :             :         /* A failed device is not called again. */
     453   [ +  +  +  + ]:       15320 :         if (!blockSequence->DeviceFailed && BlockSequence_ExceedsMaxBlocks(blockSequence))
     454                 :             :         {
     455   [ +  +  -  + ]:          67 :             *readBlockChanged = BlockSequence_DiscardOldestBlock(blockSequence) || *readBlockChanged;
     456                 :             :         }
     457                 :             :     }
     458                 :             :     else
     459                 :             :     {
     460                 :             :         /* Only a device failure gets here, and AcquireEmptyBlock has noted it: a
     461                 :             :          * full store that keeps what it has never rotates. */
     462                 :             :     }
     463                 :             : 
     464                 :       15325 :     return acquired;
     465                 :             : }
     466                 :             : 
     467                 :             : /* Dispose-then-Acquire enforces the BlockDevice contract that an Acquired block
     468                 :             :  * starts empty. Stale content can be left by a crash mid-Append on a previous
     469                 :             :  * run, or by a Dispose that succeeded after our state had already advanced
     470                 :             :  * past it. Flash drivers depend on this - Acquire = erase, and writing into
     471                 :             :  * a non-erased block corrupts data on most flash families.
     472                 :             :  *
     473                 :             :  * If Dispose fails, Acquire is not tried: a "verify-and-use" flash driver would
     474                 :             :  * reject the still-stale block anyway. The failure is noted as the operation
     475                 :             :  * that failed, so a stale block that will not go is a dispose failure. */
     476                 :       15325 : static inline bool BlockSequence_AcquireEmptyBlock(struct SolidSyslogBlockSequence* blockSequence, size_t blockIndex)
     477                 :             : {
     478                 :       15325 :     bool ready = true;
     479                 :             : 
     480         [ +  + ]:       15325 :     if (SolidSyslogBlockDevice_Exists(blockSequence->BlockDevice, blockIndex))
     481                 :             :     {
     482                 :           3 :         ready = BlockSequence_DisposeBlock(blockSequence, blockIndex);
     483                 :             : 
     484         [ +  + ]:           3 :         if (!ready)
     485                 :             :         {
     486                 :           2 :             BlockSequence_NoteDeviceFailure(blockSequence, SOLIDSYSLOG_BLOCK_STORE_ERROR_DISPOSE_FAILED);
     487                 :             :         }
     488                 :             :     }
     489                 :             : 
     490         [ +  + ]:       15325 :     if (ready)
     491                 :             :     {
     492                 :       15323 :         ready = BlockSequence_AcquireBlock(blockSequence, blockIndex);
     493                 :             : 
     494         [ +  + ]:       15323 :         if (!ready)
     495                 :             :         {
     496                 :           3 :             BlockSequence_NoteDeviceFailure(blockSequence, SOLIDSYSLOG_BLOCK_STORE_ERROR_ROTATE_FAILED);
     497                 :             :         }
     498                 :             :     }
     499                 :             : 
     500                 :       15325 :     return ready;
     501                 :             : }
     502                 :             : 
     503                 :             : /* ...and one it disposed of is not. */
     504                 :       13946 : static inline bool BlockSequence_DisposeBlock(struct SolidSyslogBlockSequence* blockSequence, size_t blockIndex)
     505                 :             : {
     506                 :       13946 :     bool disposed = SolidSyslogBlockDevice_Dispose(blockSequence->BlockDevice, blockIndex);
     507                 :             : 
     508         [ +  + ]:       13946 :     if (disposed)
     509                 :             :     {
     510                 :       13932 :         blockSequence->BlockPresent[blockIndex] = false;
     511                 :             :     }
     512                 :             : 
     513                 :       13946 :     return disposed;
     514                 :             : }
     515                 :             : 
     516                 :       15320 : static inline void BlockSequence_AdvanceWriteToNewBlock(
     517                 :             :     struct SolidSyslogBlockSequence* blockSequence,
     518                 :             :     uint8_t nextSequence
     519                 :             : )
     520                 :             : {
     521                 :       15320 :     blockSequence->WriteSequence = nextSequence;
     522                 :       15320 :     blockSequence->WritePosition = 0;
     523                 :       15320 :     blockSequence->WriteBlockCorrupt = false;
     524                 :       15320 : }
     525                 :             : 
     526                 :       15316 : static inline bool BlockSequence_ExceedsMaxBlocks(const struct SolidSyslogBlockSequence* blockSequence)
     527                 :             : {
     528                 :       15316 :     return BlockSequence_BlockCount(blockSequence) > blockSequence->MaxBlocks;
     529                 :             : }
     530                 :             : 
     531                 :             : /* The oldest block is the read block - a block the read has finished with is
     532                 :             :  * disposed of as the read leaves it - so an eviction always costs records
     533                 :             :  * still to send, and the read starts again on the block after it. */
     534                 :          67 : static bool BlockSequence_DiscardOldestBlock(struct SolidSyslogBlockSequence* blockSequence)
     535                 :             : {
     536                 :          67 :     bool readBlockChanged = false;
     537                 :             : 
     538         [ +  + ]:          67 :     if (BlockSequence_DisposeBlock(blockSequence, blockSequence->OldestSequence))
     539                 :             :     {
     540                 :          63 :         blockSequence->EvictedUnsent = true;
     541                 :          63 :         blockSequence->OldestSequence = BlockSequence_NextPresentSequence(blockSequence, blockSequence->OldestSequence);
     542                 :          63 :         BlockSequence_ResetReadToOldest(blockSequence);
     543                 :          63 :         readBlockChanged = true;
     544                 :             :     }
     545                 :             :     else
     546                 :             :     {
     547                 :           4 :         BlockSequence_NoteDeviceFailure(blockSequence, SOLIDSYSLOG_BLOCK_STORE_ERROR_DISPOSE_FAILED);
     548                 :             :     }
     549                 :             : 
     550                 :          67 :     return readBlockChanged;
     551                 :             : }
     552                 :             : 
     553                 :          63 : static void BlockSequence_ResetReadToOldest(struct SolidSyslogBlockSequence* blockSequence)
     554                 :             : {
     555                 :          63 :     BlockSequence_StartReadingBlock(blockSequence, blockSequence->OldestSequence);
     556                 :          63 : }
     557                 :             : 
     558                 :             : /* Steps over a hole in the run. Terminates because the write block is always
     559                 :             :  * present: the scan ends the run on one, and a rotation marks the one it
     560                 :             :  * acquires. */
     561                 :       13931 : static inline uint8_t BlockSequence_NextPresentSequence(
     562                 :             :     const struct SolidSyslogBlockSequence* blockSequence,
     563                 :             :     uint8_t current
     564                 :             : )
     565                 :             : {
     566                 :       13931 :     uint8_t next = BlockSequence_NextSequence(current);
     567                 :             : 
     568         [ +  + ]:       13957 :     while (!blockSequence->BlockPresent[next])
     569                 :             :     {
     570                 :          26 :         next = BlockSequence_NextSequence(next);
     571                 :             :     }
     572                 :             : 
     573                 :       13931 :     return next;
     574                 :             : }
     575                 :             : 
     576                 :      769972 : struct SolidSyslogBlockDevice* SolidSyslogBlockSequence_BlockDevice(const struct SolidSyslogBlockSequence* blockSequence
     577                 :             : )
     578                 :             : {
     579                 :      769972 :     return blockSequence->BlockDevice;
     580                 :             : }
     581                 :             : 
     582                 :      279930 : size_t SolidSyslogBlockSequence_WriteSequence(const struct SolidSyslogBlockSequence* blockSequence)
     583                 :             : {
     584                 :      279930 :     return blockSequence->WriteSequence;
     585                 :             : }
     586                 :             : 
     587                 :       20092 : size_t SolidSyslogBlockSequence_WritePosition(const struct SolidSyslogBlockSequence* blockSequence)
     588                 :             : {
     589                 :       20092 :     return blockSequence->WritePosition;
     590                 :             : }
     591                 :             : 
     592                 :      260446 : void SolidSyslogBlockSequence_NoteRecordWritten(struct SolidSyslogBlockSequence* blockSequence, size_t recordSize)
     593                 :             : {
     594                 :      260446 :     blockSequence->WritePosition += recordSize;
     595                 :      260446 :     blockSequence->AtCapacity = false;
     596                 :      260446 :     BlockSequence_NotifyThresholdCrossed(blockSequence);
     597                 :      260446 : }
     598                 :             : 
     599                 :        2650 : void SolidSyslogBlockSequence_MarkWriteBlockCorrupt(struct SolidSyslogBlockSequence* blockSequence)
     600                 :             : {
     601                 :        2650 :     blockSequence->WriteBlockCorrupt = true;
     602                 :        2650 : }
     603                 :             : 
     604                 :      259006 : size_t SolidSyslogBlockSequence_ReadSequence(const struct SolidSyslogBlockSequence* blockSequence)
     605                 :             : {
     606                 :      259006 :     return blockSequence->ReadSequence;
     607                 :             : }
     608                 :             : 
     609                 :      254278 : size_t SolidSyslogBlockSequence_ReadCursor(const struct SolidSyslogBlockSequence* blockSequence)
     610                 :             : {
     611                 :      254278 :     return blockSequence->ReadCursor;
     612                 :             : }
     613                 :             : 
     614                 :      255155 : void SolidSyslogBlockSequence_SetReadCursor(struct SolidSyslogBlockSequence* blockSequence, size_t cursor)
     615                 :             : {
     616                 :      255155 :     blockSequence->ReadCursor = cursor;
     617                 :      255155 : }
     618                 :             : 
     619                 :             : static inline bool BlockSequence_IsReadBlockActiveWrite(const struct SolidSyslogBlockSequence* blockSequence);
     620                 :             : 
     621                 :             : /* A size the device could not read is a failed device, never an empty block:
     622                 :             :  * read as empty, a block still holding unsent records would look drained. A
     623                 :             :  * block behind the write block no longer grows, so its size is read once. */
     624                 :      440831 : bool SolidSyslogBlockSequence_ReadBlockSize(struct SolidSyslogBlockSequence* blockSequence, size_t* size)
     625                 :             : {
     626                 :      440831 :     bool known = true;
     627                 :             : 
     628         [ +  + ]:      440831 :     if (BlockSequence_IsReadBlockActiveWrite(blockSequence))
     629                 :             :     {
     630                 :       84359 :         *size = blockSequence->WritePosition;
     631                 :             :     }
     632         [ +  + ]:      356472 :     else if (blockSequence->ReadBlockSizeKnown)
     633                 :             :     {
     634                 :      337056 :         *size = blockSequence->ReadBlockSize;
     635                 :             :     }
     636                 :             :     else
     637                 :             :     {
     638                 :       19416 :         known = SolidSyslogBlockDevice_Size(blockSequence->BlockDevice, blockSequence->ReadSequence, size);
     639                 :       19416 :         blockSequence->ReadBlockSize = *size;
     640                 :       19416 :         blockSequence->ReadBlockSizeKnown = known;
     641                 :             : 
     642         [ +  + ]:       19416 :         if (!known)
     643                 :             :         {
     644                 :         102 :             BlockSequence_NoteDeviceFailure(blockSequence, SOLIDSYSLOG_BLOCK_STORE_ERROR_SIZE_FAILED);
     645                 :             :         }
     646                 :             :     }
     647                 :             : 
     648                 :      440831 :     return known;
     649                 :             : }
     650                 :             : 
     651                 :      687188 : static inline bool BlockSequence_IsReadBlockActiveWrite(const struct SolidSyslogBlockSequence* blockSequence)
     652                 :             : {
     653                 :      687188 :     return blockSequence->ReadSequence == blockSequence->WriteSequence;
     654                 :             : }
     655                 :             : 
     656                 :             : static inline bool BlockSequence_IsReadBlockFullyDrained(
     657                 :             :     const struct SolidSyslogBlockSequence* blockSequence,
     658                 :             :     size_t readBlockSize
     659                 :             : );
     660                 :             : static inline void BlockSequence_AdvancePastDisposedReadBlock(struct SolidSyslogBlockSequence* blockSequence);
     661                 :             : static inline bool BlockSequence_DisposeReadBlock(struct SolidSyslogBlockSequence* blockSequence);
     662                 :             : 
     663                 :             : /* The read block is the oldest: every block before it was disposed of or
     664                 :             :  * evicted as the read left it, so disposing it leaves no hole in the run. The
     665                 :             :  * caller has checked the read is behind the write, so a next block exists. */
     666                 :        1235 : void SolidSyslogBlockSequence_LeaveReadBlock(struct SolidSyslogBlockSequence* blockSequence)
     667                 :             : {
     668                 :        1235 :     (void) BlockSequence_DisposeReadBlock(blockSequence);
     669                 :        1235 : }
     670                 :             : 
     671                 :       13876 : static inline bool BlockSequence_DisposeReadBlock(struct SolidSyslogBlockSequence* blockSequence)
     672                 :             : {
     673                 :       13876 :     bool disposed = BlockSequence_DisposeBlock(blockSequence, blockSequence->ReadSequence);
     674                 :             : 
     675         [ +  + ]:       13876 :     if (disposed)
     676                 :             :     {
     677                 :       13868 :         BlockSequence_AdvancePastDisposedReadBlock(blockSequence);
     678                 :             :     }
     679                 :             :     else
     680                 :             :     {
     681                 :           8 :         BlockSequence_NoteDeviceFailure(blockSequence, SOLIDSYSLOG_BLOCK_STORE_ERROR_DISPOSE_FAILED);
     682                 :             :     }
     683                 :             : 
     684                 :       13876 :     return disposed;
     685                 :             : }
     686                 :             : 
     687                 :       13868 : static inline void BlockSequence_AdvancePastDisposedReadBlock(struct SolidSyslogBlockSequence* blockSequence)
     688                 :             : {
     689                 :       13868 :     blockSequence->OldestSequence = BlockSequence_NextPresentSequence(blockSequence, blockSequence->OldestSequence);
     690                 :       13868 :     BlockSequence_StartReadingBlock(blockSequence, blockSequence->OldestSequence);
     691                 :       13868 :     blockSequence->AtCapacity = false;
     692                 :       13868 :     BlockSequence_NotifyThresholdCrossed(blockSequence);
     693                 :       13868 : }
     694                 :             : 
     695                 :      246357 : void SolidSyslogBlockSequence_DisposeReadBlockIfDrained(
     696                 :             :     struct SolidSyslogBlockSequence* blockSequence,
     697                 :             :     bool* readBlockChanged
     698                 :             : )
     699                 :             : {
     700                 :      246357 :     *readBlockChanged = false;
     701                 :             : 
     702         [ +  + ]:      246357 :     if (!BlockSequence_IsReadBlockActiveWrite(blockSequence))
     703                 :             :     {
     704                 :      181737 :         size_t readBlockSize = 0U;
     705                 :             : 
     706   [ +  +  +  + ]:      363471 :         if (SolidSyslogBlockSequence_ReadBlockSize(blockSequence, &readBlockSize) &&
     707                 :      181734 :             BlockSequence_IsReadBlockFullyDrained(blockSequence, readBlockSize))
     708                 :             :         {
     709                 :       12641 :             *readBlockChanged = BlockSequence_DisposeReadBlock(blockSequence);
     710                 :             :         }
     711                 :             :     }
     712                 :      246357 : }
     713                 :             : 
     714                 :      181734 : static inline bool BlockSequence_IsReadBlockFullyDrained(
     715                 :             :     const struct SolidSyslogBlockSequence* blockSequence,
     716                 :             :     size_t readBlockSize
     717                 :             : )
     718                 :             : {
     719                 :      181734 :     return blockSequence->ReadCursor >= readBlockSize;
     720                 :             : }
     721                 :             : 
     722                 :      506873 : bool SolidSyslogBlockSequence_ReadIsBehindWrite(const struct SolidSyslogBlockSequence* blockSequence)
     723                 :             : {
     724                 :      506873 :     return blockSequence->ReadSequence != blockSequence->WriteSequence;
     725                 :             : }
     726                 :             : 
     727                 :      516559 : bool SolidSyslogBlockSequence_DeviceFailed(const struct SolidSyslogBlockSequence* blockSequence)
     728                 :             : {
     729                 :      516559 :     return blockSequence->DeviceFailed;
     730                 :             : }
     731                 :             : 
     732                 :      261243 : bool SolidSyslogBlockSequence_EvictedUnsent(const struct SolidSyslogBlockSequence* blockSequence)
     733                 :             : {
     734                 :      261243 :     return blockSequence->EvictedUnsent;
     735                 :             : }
     736                 :             : 
     737                 :         378 : enum SolidSyslogBlockStoreErrors SolidSyslogBlockSequence_FailedOperation(
     738                 :             :     const struct SolidSyslogBlockSequence* blockSequence
     739                 :             : )
     740                 :             : {
     741                 :         378 :     return blockSequence->FailedOperation;
     742                 :             : }
     743                 :             : 
     744                 :      484911 : bool SolidSyslogBlockSequence_HasUnsent(const struct SolidSyslogBlockSequence* blockSequence)
     745                 :             : {
     746         [ +  + ]:      638892 :     return SolidSyslogBlockSequence_ReadIsBehindWrite(blockSequence) ||
     747         [ +  + ]:      153981 :            (blockSequence->ReadCursor < blockSequence->WritePosition);
     748                 :             : }
     749                 :             : 
     750                 :         246 : size_t SolidSyslogBlockSequence_TotalBytes(const struct SolidSyslogBlockSequence* blockSequence)
     751                 :             : {
     752                 :         246 :     return blockSequence->MaxBlocks * blockSequence->MaxBlockSize;
     753                 :             : }
     754                 :             : 
     755                 :        3013 : size_t SolidSyslogBlockSequence_UsedBytes(const struct SolidSyslogBlockSequence* blockSequence)
     756                 :             : {
     757                 :        3013 :     size_t used = 0U;
     758                 :             : 
     759         [ +  + ]:        3013 :     if (blockSequence->AtCapacity)
     760                 :             :     {
     761                 :         237 :         used = SolidSyslogBlockSequence_TotalBytes(blockSequence);
     762                 :             :     }
     763                 :             :     else
     764                 :             :     {
     765                 :        2776 :         size_t closedBlocks = BlockSequence_BlockCount(blockSequence) - 1U;
     766                 :        2776 :         used = (closedBlocks * blockSequence->MaxBlockSize) + blockSequence->WritePosition;
     767                 :             :     }
     768                 :             : 
     769                 :        3013 :     return used;
     770                 :             : }
        

Generated by: LCOV version 2.0-1