• Home
  • Features
  • Pricing
  • Docs
  • Announcements
  • Sign In

randombit / botan / 27253840577

08 Jun 2026 09:36PM UTC coverage: 89.367% (+0.01%) from 89.356%
27253840577

push

github

web-flow
Merge pull request #5657 from randombit/jack/ctrl-escaping

Escape control characters in codec error reporting and DN printing

110761 of 123940 relevant lines covered (89.37%)

11052282.15 hits per line

Source File
Press 'n' to go to next uncovered line, 'b' for previous

92.41
/src/lib/codec/hex/hex.cpp
1
/*
2
* Hex Encoding and Decoding
3
* (C) 2010,2020 Jack Lloyd
4
*
5
* Botan is released under the Simplified BSD License (see license.txt)
6
*/
7

8
#include <botan/hex.h>
9

10
#include <botan/exceptn.h>
11
#include <botan/mem_ops.h>
12
#include <botan/internal/charset.h>
13
#include <botan/internal/fmt.h>
14
#include <botan/internal/int_utils.h>
15
#include <botan/internal/loadstor.h>
16

17
namespace Botan {
18

19
namespace {
20

21
uint16_t hex_encode_2nibble(uint8_t n8, bool uppercase) {
1,226,677✔
22
   // Offset for upper or lower case 'a' resp
23
   const uint16_t a_mask = uppercase ? 0x0707 : 0x2727;
2,453,354✔
24

25
   const uint16_t n = (static_cast<uint16_t>(n8 & 0xF0) << 4) | (n8 & 0x0F);
1,226,677✔
26
   // n >= 10? If so add offset
27
   const uint16_t diff = swar_lt<uint16_t>(0x0909, n) & a_mask;
1,226,677✔
28
   // Can't overflow between bytes, so don't need explicit SWAR addition:
29
   return n + 0x3030 + diff;
1,226,677✔
30
}
31

32
}  // namespace
33

34
void hex_encode(char output[], const uint8_t input[], size_t input_length, bool uppercase) {
45,303✔
35
   for(size_t i = 0; i != input_length; ++i) {
1,271,980✔
36
      const uint16_t h = hex_encode_2nibble(input[i], uppercase);
1,226,677✔
37
      output[2 * i] = get_byte<0>(h);
1,226,677✔
38
      output[2 * i + 1] = get_byte<1>(h);
1,226,677✔
39
   }
40
}
45,303✔
41

42
std::string hex_encode(const uint8_t input[], size_t input_length, bool uppercase) {
45,866✔
43
   const size_t output_length = mul_or_throw<size_t>(2, input_length, "Input too large to hex encode");
45,866✔
44
   std::string output(output_length, 0);
45,866✔
45

46
   if(input_length > 0) {
45,866✔
47
      hex_encode(&output.front(), input, input_length, uppercase);
45,263✔
48
   }
49

50
   return output;
45,866✔
51
}
×
52

53
namespace {
54

55
uint8_t hex_char_to_bin(char input) {
34,225,050✔
56
   // Starts of valid value ranges (v_lo) and their lengths (v_range)
57
   constexpr uint64_t v_lo = make_uint64(0, '0', 'a', 'A', ' ', '\n', '\t', '\r');
34,225,050✔
58
   constexpr uint64_t v_range = make_uint64(0, 10, 6, 6, 1, 1, 1, 1);
34,225,050✔
59
   constexpr uint64_t expand8 = 0x0101010101010101;
34,225,050✔
60
   constexpr uint64_t top64 = 0x8000000000000000;
34,225,050✔
61

62
   const uint8_t x = static_cast<uint8_t>(input);
34,225,050✔
63
   const uint64_t x8 = x * expand8;
34,225,050✔
64

65
   const uint64_t v_mask = swar_in_range<uint64_t>(x8, v_lo, v_range) ^ top64;
34,225,050✔
66

67
   // This is the offset added to x to get the value we need
68
   const uint64_t val_v = 0xd0a9c960767773 ^ static_cast<uint64_t>(0xFF - x) << 56;
34,225,050✔
69

70
   return x + static_cast<uint8_t>(val_v >> (8 * index_of_first_set_byte(v_mask)));
34,225,050✔
71
}
72

73
}  // namespace
74

75
size_t hex_decode(uint8_t output[], const char input[], size_t input_length, size_t& input_consumed, bool ignore_ws) {
233,219✔
76
   uint8_t* out_ptr = output;
233,219✔
77
   bool top_nibble = true;
233,219✔
78
   uint8_t next = 0;
233,219✔
79

80
   input_consumed = 0;
233,219✔
81

82
   clear_mem(output, input_length / 2);
233,219✔
83

84
   for(size_t i = 0; i != input_length; ++i) {
34,458,260✔
85
      const uint8_t bin = hex_char_to_bin(input[i]);
34,225,049✔
86

87
      if(bin >= 0x10) {
34,225,049✔
88
         if(bin == 0x80 && ignore_ws) {
9,544✔
89
            continue;
9,536✔
90
         }
91

92
         throw Invalid_Argument(fmt("hex_decode: invalid character '{}'", format_char_for_display(input[i])));
16✔
93
      }
94

95
      if(top_nibble) {
34,215,505✔
96
         next = bin << 4;
17,107,753✔
97
      } else {
98
         next |= bin;
17,107,752✔
99
         *out_ptr = next;
17,107,752✔
100
      }
101

102
      top_nibble = !top_nibble;
34,215,505✔
103
      if(top_nibble) {
34,215,505✔
104
         ++out_ptr;
17,107,752✔
105
         input_consumed = i + 1;
17,107,752✔
106
      }
107
   }
108

109
   /*
110
   * Consume trailing whitespace following the last full byte; a leftover
111
   * unpaired nibble (if any) stops the scan and is left unconsumed.
112
   */
113
   while(input_consumed < input_length && hex_char_to_bin(input[input_consumed]) == 0x80) {
233,212✔
114
      ++input_consumed;
1✔
115
   }
116

117
   return (out_ptr - output);
233,211✔
118
}
119

120
size_t hex_decode(uint8_t output[], const char input[], size_t input_length, bool ignore_ws) {
233,173✔
121
   size_t consumed = 0;
233,173✔
122
   const size_t written = hex_decode(output, input, input_length, consumed, ignore_ws);
233,173✔
123

124
   if(consumed != input_length) {
233,165✔
125
      throw Invalid_Argument("hex_decode: input did not have full bytes");
×
126
   }
127

128
   return written;
233,165✔
129
}
130

131
size_t hex_decode(uint8_t output[], std::string_view input, bool ignore_ws) {
2,841✔
132
   return hex_decode(output, input.data(), input.length(), ignore_ws);
2,841✔
133
}
134

135
size_t hex_decode(std::span<uint8_t> output, std::string_view input, bool ignore_ws) {
×
136
   if(output.size() < input.length() / 2) {
×
137
      throw Invalid_Argument("hex_decode: output buffer too small");
×
138
   }
139
   return hex_decode(output.data(), input.data(), input.length(), ignore_ws);
×
140
}
141

142
secure_vector<uint8_t> hex_decode_locked(const char input[], size_t input_length, bool ignore_ws) {
45,027✔
143
   secure_vector<uint8_t> bin(1 + input_length / 2);
45,027✔
144

145
   const size_t written = hex_decode(bin.data(), input, input_length, ignore_ws);
45,027✔
146

147
   bin.resize(written);
45,026✔
148
   return bin;
45,026✔
149
}
1✔
150

151
secure_vector<uint8_t> hex_decode_locked(std::string_view input, bool ignore_ws) {
39,921✔
152
   return hex_decode_locked(input.data(), input.size(), ignore_ws);
39,921✔
153
}
154

155
std::vector<uint8_t> hex_decode(const char input[], size_t input_length, bool ignore_ws) {
185,305✔
156
   std::vector<uint8_t> bin(1 + input_length / 2);
185,305✔
157

158
   const size_t written = hex_decode(bin.data(), input, input_length, ignore_ws);
185,305✔
159

160
   bin.resize(written);
185,298✔
161
   return bin;
185,298✔
162
}
7✔
163

164
std::vector<uint8_t> hex_decode(std::string_view input, bool ignore_ws) {
185,297✔
165
   return hex_decode(input.data(), input.size(), ignore_ws);
185,297✔
166
}
167

168
}  // namespace Botan
STATUS · Troubleshooting · Open an Issue · Sales · Support · CAREERS · ENTERPRISE · START FREE · SCHEDULE DEMO
ANNOUNCEMENTS · TWITTER · TOS & SLA · Supported CI Services · What's a CI service? · Automated Testing

© 2026 Coveralls, Inc