Bitcoin ABC  0.29.12
P2P Digital Currency
serialize_intcode.h
Go to the documentation of this file.
1 // Copyright (c) 2024 The Bitcoin developers
2 // Distributed under the MIT software license, see the accompanying
3 // file COPYING or http://www.opensource.org/licenses/mit-license.php.
4 
5 #ifndef BITCOIN_SERIALIZE_INTCODE_H
6 #define BITCOIN_SERIALIZE_INTCODE_H
7 
8 #include <crypto/common.h>
9 #include <serialize.h>
10 #include <tinyformat.h>
11 
12 #include <cstdint>
13 #include <ios>
14 
15 const uint64_t VALID_RANGE[] = {
16  0,
17  0x80,
18  0x4000,
19  0x20'0000,
20  0x1000'0000,
21  0x08'0000'0000,
22  0x0400'0000'0000,
23  0x02'0000'0000'0000,
24  0x0100'0000'0000'0000,
25 };
26 
46 template <typename Stream> void WriteIntcode(Stream &os, uint64_t value) {
47  // Integers below 0x80 are just represented by themselves
48  if (value < 0x80) {
49  ser_writedata8(os, value);
50  return;
51  }
52 
53  // Number of bits required to represent `value`
54  const uint64_t numBits = CountBits(value);
55  // Number of bytes required to represent `value`
56  uint64_t numBytes = (numBits - 1) / 7;
57  if (numBytes >= 8) {
58  // For 0xff headers, the formula breaks, as it's not followed by a 0 bit
59  ser_writedata8(os, 0xff);
60  numBytes = 8;
61  } else {
62  // Bits to represent how many bytes are used for the data
63  const uint64_t header = (0xff << (8 - numBytes)) & 0xff;
64  // Prepend the header
65  value |= header << (8 * numBytes);
66  // Header adds 1 leading byte
67  numBytes++;
68  // Left align
69  value <<= 8 * (8 - numBytes);
70  }
71 
72  // Write remaining bytes as big-endian
73  for (size_t i = 0; i < numBytes; ++i) {
74  ser_writedata8(os, value >> 56);
75  value <<= 8;
76  }
77 }
78 
84 template <typename Stream> uint64_t ReadIntcode(Stream &is) {
85  // Read the header byte
86  const uint8_t header = ser_readdata8(is);
87 
88  // If the first bit is not set, the number is the first byte itself
89  if (header < 0x80) {
90  return header;
91  }
92 
93  // Number of leading ones in header (represents the number of extra bytes)
94  const uint64_t leadingOnes = 8 - CountBits(uint8_t(~header));
95 
96  // Read the data bits from the header
97  const uint8_t mask = 0xff >> leadingOnes;
98  uint64_t result = header & mask;
99 
100  // Read remaining bytes as big-endian
101  for (size_t i = 0; i < leadingOnes; ++i) {
102  result <<= 8;
103  result |= ser_readdata8(is);
104  }
105 
106  if (result < VALID_RANGE[leadingOnes]) {
107  throw std::ios_base::failure(strprintf(
108  "non-canonical ReadMitraInt(): 0x%016x out of range for %d "
109  "leading ones",
110  result, leadingOnes));
111  }
112 
113  return result;
114 }
115 
116 #endif // BITCOIN_SERIALIZE_INTCODE_H
static uint64_t CountBits(uint64_t x)
Return the smallest number n such that (x >> n) == 0 (or 64 if the highest bit in x is set.
Definition: common.h:82
uint8_t ser_readdata8(Stream &s)
Definition: serialize.h:83
void ser_writedata8(Stream &s, uint8_t obj)
Lowest-level serialization and conversion.
Definition: serialize.h:55
const uint64_t VALID_RANGE[]
uint64_t ReadIntcode(Stream &is)
Read a 64-bit integer as intcode, see WriteIntcode.
void WriteIntcode(Stream &os, uint64_t value)
Write a 64-bit integer in intcode encoding:
#define strprintf
Format arguments and return the string or write to given std::ostream (see tinyformat::format doc for...
Definition: tinyformat.h:1202