Bitcoin ABC 0.32.5
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 <bit>
13#include <cstdint>
14#include <ios>
15
16const uint64_t VALID_RANGE[] = {
17 0,
18 0x80,
19 0x4000,
20 0x20'0000,
21 0x1000'0000,
22 0x08'0000'0000,
23 0x0400'0000'0000,
24 0x02'0000'0000'0000,
25 0x0100'0000'0000'0000,
26};
27
47template <typename Stream> void WriteIntcode(Stream &os, uint64_t value) {
48 // Integers below 0x80 are just represented by themselves
49 if (value < 0x80) {
50 ser_writedata8(os, value);
51 return;
52 }
53
54 // Number of bits required to represent `value`
55 const uint64_t numBits = std::bit_width(value);
56 // Number of bytes required to represent `value`
57 uint64_t numBytes = (numBits - 1) / 7;
58 if (numBytes >= 8) {
59 // For 0xff headers, the formula breaks, as it's not followed by a 0 bit
60 ser_writedata8(os, 0xff);
61 numBytes = 8;
62 } else {
63 // Bits to represent how many bytes are used for the data
64 const uint64_t header = (0xff << (8 - numBytes)) & 0xff;
65 // Prepend the header
66 value |= header << (8 * numBytes);
67 // Header adds 1 leading byte
68 numBytes++;
69 // Left align
70 value <<= 8 * (8 - numBytes);
71 }
72
73 // Write remaining bytes as big-endian
74 for (size_t i = 0; i < numBytes; ++i) {
75 ser_writedata8(os, value >> 56);
76 value <<= 8;
77 }
78}
79
85template <typename Stream> uint64_t ReadIntcode(Stream &is) {
86 // Read the header byte
87 const uint8_t header = ser_readdata8(is);
88
89 // If the first bit is not set, the number is the first byte itself
90 if (header < 0x80) {
91 return header;
92 }
93
94 // Number of leading ones in header (represents the number of extra bytes)
95 const uint64_t leadingOnes = 8 - std::bit_width(uint8_t(~header));
96
97 // Read the data bits from the header
98 const uint8_t mask = 0xff >> leadingOnes;
99 uint64_t result = header & mask;
100
101 // Read remaining bytes as big-endian
102 for (size_t i = 0; i < leadingOnes; ++i) {
103 result <<= 8;
104 result |= ser_readdata8(is);
105 }
106
107 if (result < VALID_RANGE[leadingOnes]) {
108 throw std::ios_base::failure(strprintf(
109 "non-canonical ReadMitraInt(): 0x%016x out of range for %d "
110 "leading ones",
111 result, leadingOnes));
112 }
113
114 return result;
115}
116
117#endif // BITCOIN_SERIALIZE_INTCODE_H
uint8_t ser_readdata8(Stream &s)
Definition: serialize.h:86
void ser_writedata8(Stream &s, uint8_t obj)
Lowest-level serialization and conversion.
Definition: serialize.h:58
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