Matter SDK Coverage Report
Current view: top level - lib/support - Base85.cpp (source / functions) Coverage Total Hit
Test: SHA:3f9cd168e84cd831b7699126f5296f5c5498690f Lines: 99.2 % 124 123
Test Date: 2026-04-27 19:52:19 Functions: 100.0 % 8 8

            Line data    Source code
       1              : /*
       2              :  *
       3              :  *    Copyright (c) 2026 Project CHIP Authors
       4              :  *    All rights reserved.
       5              :  *
       6              :  *    Licensed under the Apache License, Version 2.0 (the "License");
       7              :  *    you may not use this file except in compliance with the License.
       8              :  *    You may obtain a copy of the License at
       9              :  *
      10              :  *        http://www.apache.org/licenses/LICENSE-2.0
      11              :  *
      12              :  *    Unless required by applicable law or agreed to in writing, software
      13              :  *    distributed under the License is distributed on an "AS IS" BASIS,
      14              :  *    WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
      15              :  *    See the License for the specific language governing permissions and
      16              :  *    limitations under the License.
      17              :  */
      18              : 
      19              : /**
      20              :  *    @file
      21              :  *      Base-85 utility functions (RFC 1924 alphabet, git/Python compatible).
      22              :  */
      23              : 
      24              : #include "Base85.h"
      25              : 
      26              : #include <string.h>
      27              : 
      28              : namespace chip {
      29              : 
      30              : // RFC 1924 alphabet: 0-9 A-Z a-z !#$%&()*+-;<=>?@^_`{|}~
      31              : // Values 0-9 map to '0'-'9', 10-35 to 'A'-'Z', 36-61 to 'a'-'z',
      32              : // and values 62-84 map to the 23 punctuation characters below.
      33              : 
      34              : // Convert a value in the range 0..84 to its RFC 1924 base85 character.
      35          133 : static char Base85ValToChar(uint8_t val)
      36              : {
      37          133 :     switch (val)
      38              :     {
      39              :     // clang-format off
      40            1 :     case 62: return '!';
      41            1 :     case 63: return '#';
      42            1 :     case 64: return '$';
      43            3 :     case 65: return '%';
      44            2 :     case 66: return '&';
      45            1 :     case 67: return '(';
      46            2 :     case 68: return ')';
      47            1 :     case 69: return '*';
      48            2 :     case 70: return '+';
      49            6 :     case 71: return '-';
      50            1 :     case 72: return ';';
      51            2 :     case 73: return '<';
      52            2 :     case 74: return '=';
      53            1 :     case 75: return '>';
      54            1 :     case 76: return '?';
      55            2 :     case 77: return '@';
      56            2 :     case 78: return '^';
      57            2 :     case 79: return '_';
      58            2 :     case 80: return '`';
      59            2 :     case 81: return '{';
      60            5 :     case 82: return '|';
      61            2 :     case 83: return '}';
      62            2 :     case 84: return '~';
      63              :     // clang-format on
      64           87 :     default:
      65           87 :         if (val < 10)
      66              :         {
      67           26 :             return static_cast<char>('0' + val);
      68              :         }
      69           61 :         if (10 <= val && val < 36)
      70              :         {
      71           30 :             return static_cast<char>('A' + val - 10);
      72              :         }
      73           31 :         if (36 <= val && val < 62)
      74              :         {
      75           31 :             return static_cast<char>('a' + val - 36);
      76              :         }
      77              :     }
      78            0 :     return 0; // not possible
      79              : }
      80              : 
      81              : // Convert an RFC 1924 base85 character to a value in the range 0..84,
      82              : // or UINT8_MAX if the character is not in the alphabet.
      83          134 : static uint8_t Base85CharToVal(char c)
      84              : {
      85          134 :     switch (c)
      86              :     {
      87              :     // clang-format off
      88            1 :     case '!': return 62;
      89            1 :     case '#': return 63;
      90            1 :     case '$': return 64;
      91            2 :     case '%': return 65;
      92            2 :     case '&': return 66;
      93            1 :     case '(': return 67;
      94            2 :     case ')': return 68;
      95            1 :     case '*': return 69;
      96            2 :     case '+': return 70;
      97            5 :     case '-': return 71;
      98            1 :     case ';': return 72;
      99            2 :     case '<': return 73;
     100            2 :     case '=': return 74;
     101            1 :     case '>': return 75;
     102            1 :     case '?': return 76;
     103            2 :     case '@': return 77;
     104            2 :     case '^': return 78;
     105            2 :     case '_': return 79;
     106            2 :     case '`': return 80;
     107            1 :     case '{': return 81;
     108            4 :     case '|': return 82;
     109            1 :     case '}': return 83;
     110            7 :     case '~': return 84;
     111              :     // clang-format on
     112           88 :     default:
     113           88 :         if (c >= '0' && c <= '9')
     114              :         {
     115           30 :             return static_cast<uint8_t>(c - '0');
     116              :         }
     117           58 :         if (c >= 'A' && c <= 'Z')
     118              :         {
     119           28 :             return static_cast<uint8_t>(c - 'A' + 10);
     120              :         }
     121           30 :         if (c >= 'a' && c <= 'z')
     122              :         {
     123           27 :             return static_cast<uint8_t>(c - 'a' + 36);
     124              :         }
     125              :     }
     126            3 :     return UINT8_MAX;
     127              : }
     128              : 
     129           25 : static uint32_t ReadGroup(const uint8_t * src, size_t count)
     130              : {
     131           25 :     uint8_t padded[4] = { 0 };
     132           25 :     memcpy(padded, src, count);
     133           25 :     return (static_cast<uint32_t>(padded[0]) << 24) | //
     134           25 :         (static_cast<uint32_t>(padded[1]) << 16) |    //
     135           25 :         (static_cast<uint32_t>(padded[2]) << 8) |     //
     136           25 :         (static_cast<uint32_t>(padded[3]));
     137              : }
     138              : 
     139           25 : static void WriteGroup(uint32_t value, uint8_t * dest, size_t count)
     140              : {
     141              :     uint8_t decoded[4] = {
     142           25 :         static_cast<uint8_t>(value >> 24),
     143           25 :         static_cast<uint8_t>(value >> 16),
     144           25 :         static_cast<uint8_t>(value >> 8),
     145              :         static_cast<uint8_t>(value),
     146           25 :     };
     147           25 :     memcpy(dest, decoded, count);
     148           25 : }
     149              : 
     150           31 : static bool DecodeGroup(const char * src, size_t count, uint32_t & outValue)
     151              : {
     152              :     // "|NsC0" is the largest allowed group value (UINT32_MAX); we could
     153              :     // check against this value here directly, but it is simpler to do the
     154              :     // calculation in a uint64_t and validate the decoded result.
     155           31 :     uint64_t value = 0;
     156          162 :     for (size_t j = 0; j < count; j++)
     157              :     {
     158          134 :         uint8_t digit = Base85CharToVal(src[j]);
     159          134 :         VerifyOrReturnValue(digit != UINT8_MAX, false);
     160          131 :         value = value * 85 + digit;
     161              :     }
     162           43 :     for (size_t j = count; j < 5; j++)
     163              :     {
     164           15 :         value = value * 85 + 84; // implicitly pad to 5 "digits" with '~' (84)
     165              :     }
     166           28 :     VerifyOrReturnValue(value <= UINT32_MAX, false);
     167           26 :     outValue = static_cast<uint32_t>(value);
     168           26 :     return true;
     169              : }
     170              : 
     171           32 : static void EncodeGroup(uint32_t value, char * out, size_t count)
     172              : {
     173           59 :     for (size_t j = 5; j-- > count;)
     174              :     {
     175           27 :         value /= 85; // truncate least significant "digits"
     176              :     }
     177          165 :     for (size_t j = count; j-- > 0;)
     178              :     {
     179          133 :         out[j] = Base85ValToChar(static_cast<uint8_t>(value % 85));
     180          133 :         value /= 85;
     181              :     }
     182           32 : }
     183              : 
     184           16 : CHIP_ERROR BytesToBase85(const uint8_t * src, size_t srcSize, char * dest, size_t destSize)
     185              : {
     186           16 :     VerifyOrReturnError(src != nullptr || srcSize == 0, CHIP_ERROR_INVALID_ARGUMENT);
     187           15 :     VerifyOrReturnError(dest != nullptr, CHIP_ERROR_INVALID_ARGUMENT);
     188              :     // Base85EncodedLength(srcSize) could overflow / saturate to SIZE_MAX,
     189              :     // checking via Base85DecodedLength(destSize) instead avoids this issue.
     190           14 :     VerifyOrReturnError(Base85DecodedLength(destSize) >= srcSize, CHIP_ERROR_BUFFER_TOO_SMALL);
     191              : 
     192           13 :     size_t remainder = srcSize % 4;
     193           32 :     for (auto * end = src + (srcSize - remainder); src < end; src += 4, dest += 5)
     194              :     {
     195           19 :         EncodeGroup(ReadGroup(src, 4), dest, 5);
     196              :     }
     197           13 :     if (remainder > 0)
     198              :     {
     199            6 :         EncodeGroup(ReadGroup(src, remainder), dest, remainder + 1);
     200              :     }
     201           13 :     return CHIP_NO_ERROR;
     202              : }
     203              : 
     204           23 : CHIP_ERROR Base85ToBytes(const char * src, size_t srcSize, uint8_t * dest, size_t destSize)
     205              : {
     206           23 :     VerifyOrReturnError(src != nullptr || srcSize == 0, CHIP_ERROR_INVALID_ARGUMENT);
     207           22 :     VerifyOrReturnError(dest != nullptr, CHIP_ERROR_INVALID_ARGUMENT);
     208           21 :     VerifyOrReturnError(destSize >= Base85DecodedLength(srcSize), CHIP_ERROR_BUFFER_TOO_SMALL);
     209              : 
     210              :     uint32_t value;
     211           21 :     size_t remainder = srcSize % 5;
     212           21 :     VerifyOrReturnError(remainder != 1, CHIP_ERROR_INVALID_ARGUMENT); // 1 "digit" does not encode a full byte
     213           38 :     for (auto * end = src + (srcSize - remainder); src < end; src += 5, dest += 4)
     214              :     {
     215           24 :         VerifyOrReturnError(DecodeGroup(src, 5, value), CHIP_ERROR_INVALID_ARGUMENT);
     216           19 :         WriteGroup(value, dest, 4);
     217              :     }
     218           14 :     if (remainder > 0) // 2..4
     219              :     {
     220            7 :         VerifyOrReturnError(DecodeGroup(src, remainder, value), CHIP_ERROR_INVALID_ARGUMENT);
     221              : 
     222              :         char check[4];
     223            7 :         uint32_t padding = UINT32_MAX >> (8 * (remainder - 1));
     224            7 :         EncodeGroup(value & ~padding, check, remainder);
     225            7 :         VerifyOrReturnError(memcmp(src, check, remainder) == 0, CHIP_ERROR_INVALID_ARGUMENT);
     226              : 
     227            6 :         WriteGroup(value, dest, remainder - 1); // might clobber src
     228              :     }
     229           13 :     return CHIP_NO_ERROR;
     230              : }
     231              : 
     232              : } // namespace chip
        

Generated by: LCOV version 2.0-1