1

The rust project I m using depends on fixed_hash 0.2.2. And I would need to compare one H160 against a literal (mode exactly know if my_var==0xdac17f958d2ee523a2206206994597c13d831ec7).

Internally, the H160 type is just a pointer to a plain integer encoded like with _Extint(). And as I already need to pass the value to a C module, I m thinking about just making the comparison from there.

The problem is integer litterals in clang are read as 64 bits Integers, so that

const _ExtInt(160) my_const=0xdac17f958d2ee523a2206206994597c13d831ec7;

fails with

<source>:1:28: error: integer literal is too large to be represented in any integer type

So how to assign 0xdac17f958d2ee523a2206206994597c13d831ec7 to my_const in big endian?

user2284570
  • 2,891
  • 3
  • 26
  • 74
  • Is it your own type or a type from some library? Please provide a minimal reproducible example. – PitaJ Dec 15 '20 at 16:21
  • @PitaJ do you mean `_Extint()`? It s a nonstandard native type supported by clang. https://blog.llvm.org/2020/04/the-new-clang-extint-feature-provides.html – user2284570 Dec 15 '20 at 16:29
  • Your link suggests that `_ExtInt` literals are not implemented (last two paragraphs under "Future Extensions"). – Nate Eldredge Dec 15 '20 at 16:48
  • @NateEldredge but 6 months later, it s part of clang version 12. – user2284570 Dec 15 '20 at 16:52
  • `but 6 months later, it s part of clang version 12` Do you have any reference for that? If it is, why don't you use it? – KamilCuk Dec 15 '20 at 16:55
  • @KamilCuk for references, the goldbot link in my question? The problem is it doesn t provide a way to assign values to integer larger than 128. – user2284570 Dec 15 '20 at 16:56

2 Answers2

2

Construct it from smaller values and shift.

const _ExtInt(160) my_const = 
    (unsigned _ExtInt(160))0xdac17f95ull << 128 |
    (unsigned _ExtInt(160))0x8d2ee523a2206206ull << 64 |
    (unsigned _ExtInt(160))0x994597c13d831ec7ull << 0;
KamilCuk
  • 120,984
  • 8
  • 59
  • 111
  • And how to do it in big endian when the machine is in little endian? (Which is my case since H160 from rust in in big endian) – user2284570 Dec 15 '20 at 16:57
  • I'd imagine `_ExtInt(160)` will always be native-endian. If you want to ensure the data is big-endian, just pass a byte array around instead. – PitaJ Dec 15 '20 at 17:14
  • `And how to do it in big endian when the machine is in little endian?` I do not understand. C representation of values is always MSB on the left, irrelevant of endianess. If you want to switch specific bytes of the value which will result in specific bytes stored at specific locations on specific architecture, that's just a mapping and a mathematical operation. C is well suited to do mathematics, with it's `<<` `>>` and `&` `|` operators. – KamilCuk Dec 15 '20 at 18:45
  • @KamilCuk the _ExtInt to which it will be compared to is received from the network as a byte array. And x86 is little endian. Hence the need to assign the constant to `my_const` in the network byte order. – user2284570 Dec 16 '20 at 21:19
  • @PitaJ How should I pass a byte array to it? – user2284570 Dec 16 '20 at 21:20
  • How is that relevant? If it's received "as a byte array" for sure it's not a literal and there no need to initialize anything. If it's a byte array, like `unsigned char arr[20];` then `_ExtInt(160) result = (_ExtInt(160))arr[0] << 152 | (_ExtInt(160))arr[1] << 148 | etc. etc. | (_ExtInt(160))arr[19] << 0;`. Converting from a "byte array in little endian" to a value is a mathematical operation. `it will be compared` - if it's only to be compared, there's no need to use any `_ExtInt` extension. Just have a byte array and use `memcmp`. – KamilCuk Dec 16 '20 at 21:20
  • @KamilCuk I m meaning that instead of converting the endianness of the source number received from the network , I want `my_const` to be stored on the composite endianness of my machine. – user2284570 Dec 16 '20 at 21:24
  • Then convert it. glibc `endian.h` implements most common operations - like `le64toh` or `htobe64` etc. - for standard integers up to 64-bits. [See how it's done there](https://github.com/lattera/glibc/blob/master/bits/byteswap.h#L59) and write your own function for `_ExtInt(160)` type. Would be a short macro with about ~20 lines. – KamilCuk Dec 16 '20 at 21:25
  • @KamilCuk But instead of having a number in the reverse byte order in my source code, I would like to maintain legibility. What would be a working macro for this? Especially given `my_const` is an exported symbol. **And that global variables can t be initialized using a function**. – user2284570 Dec 16 '20 at 21:31
  • Write a macro `#define bswap_constant_160(x) ...` in the same fashion as in the glibc source code, then do `const _ExtInt(160) my_const = bswap_constant_160( (unsigned _ExtInt(160))0xdac17f95ull << 128 | (unsigned _ExtInt(160))0x8d2ee523a2206206ull << 64 | (unsigned _ExtInt(160))0x994597c13d831ec7ull << 0 )`. `that global variables can t be initialized using a function` Yes, but macros may expand to constant expression, which in turn may be used to initialize static variables. – KamilCuk Dec 16 '20 at 21:39
  • @KamilCuk no, not all type of macros `error: initializer element is not a compile time constant`. – user2284570 Dec 16 '20 at 21:58
1

You can add a routine to parse it from a string. In C++, this can be done at compile time:

#include <stdexcept>

template <class T>
constexpr auto parseInt(const char* str) {
    T res = 0;
    while (*str) {
        const char ch = *str++;
        if (ch < '0' || ch > '9') {
            throw std::runtime_error("String does not represent an int");
        }
        res *= 10;
        res += (ch - '0');
    }
    return res;
}

int main() {
    constexpr auto my_const = parseInt<unsigned _BitInt(160)>("1248875146012964071876423320777688075155124985543");
}
Aykhan Hagverdili
  • 28,141
  • 6
  • 41
  • 93