fkYAML icon indicating copy to clipboard operation
fkYAML copied to clipboard

Issue with parsing unsigned numbers

Open sndth opened this issue 5 months ago • 10 comments

Description

Hello! First of all, I'd like to thank you once again for improving this library!❤️

I have a small issue with loading unsigned numbers in a document - for example, when trying to load a document that contains a number which is a 64-bit xxHash cipher, I get the following exception:

Image

It seems that the project doesn't currently support unsigned numbers (std::uint64_t). I tried to add support myself, but the end result always ends in failure.

However, I've made a few potentially useful changes that could make it easier to introduce a new type - unless we come up with some other solution?

Reproduction steps

Please use this source code:

#include <iostream>
#include <fkYAML/node.hpp>

int
main()
{
  auto yaml = fkyaml::node::deserialize("x: 15745692345339290292");
  std::cout << yaml["x"].as_int();
}

Expected vs. actual results

Expected result is parse correctly unsigned integer, actual result is an exception.

Minimal code example

I attached earlier

Error messages

I attached earlier

Compiler and operating system

Windows 11, Latest MSVC

Library version

develop

Validation

sndth avatar Jul 24 '25 12:07 sndth

Hi, did you ever solve this? I need to save a uint64_t and I don't know how. Thanks!

Sebight avatar Sep 22 '25 13:09 Sebight

Hello, I'll try to deal again with this issue, if I succeed I'll let you know!

sndth avatar Sep 22 '25 21:09 sndth

Hi @Sebight, I have good news. I managed to add basic support for unsigned numbers. For now, it works only with to_node, so from_node might still be an issue, but with the new functions as_uint() and is_unsigned_integer() we can already extract unsigned values from a node.

I haven't written unit tests yet because I don't have enough time - if you'd like to help, I'd really appreciate it! Anyway, here's a simple example:

#include <iostream>
#include <fkYAML/node.hpp>

int
main()
{
  auto test = fkyaml::node::deserialize("x: 2147483648\ny: 15745692345339290292");

  std::cout << test["x"].as_uint() << '\n'; // 2147483648
  std::cout << test["x"].is_integer() << '\n'; // 1
  std::cout << test["x"].is_unsigned_integer() << "\n\n"; // 1 (integer is treated as unsigned like nlohmann)

  std::cout << test["y"].as_uint() << '\n'; // 15745692345339290292
  std::cout << test["y"].is_integer() << '\n'; // 0
  std::cout << test["y"].is_unsigned_integer() << "\n\n"; // 1
}

sndth avatar Sep 23 '25 23:09 sndth

Hello @Sebight. Did you tested my branch? Thank you 😄

sndth avatar Sep 25 '25 20:09 sndth

Hi @sndth, really sorry, I have decided in the end to go with JSON. I could still test the branch when I find a moment, but I won't probably be needing a yaml library in the near future. 😅

Sebight avatar Sep 26 '25 13:09 Sebight

Hey @sndth, I thought I wouldn't run into this problem because I was using get_value<uint64_t>() instead of as_int(), but if my ID has 20 digits (most significant bit set to 1), it fails to parse. But if it has 19, it doesn't fail, probably because it fits in a int64_t.

Is this a bug as well? The easiest fix for me would be to truncate the value, I guess there would be no problems with conflicts even when losing a bit.

MirrasHue avatar Sep 26 '25 21:09 MirrasHue

Hello @MirrasHue, thanks for you report! Currently I recommend using as_uint() from my branch. I'll take a look into the get_value function. Of course, this is a bug :(

sndth avatar Sep 26 '25 22:09 sndth

Hey @sndth, I thought I wouldn't run into this problem because I was using get_value<uint64_t>() instead of as_int(), but if my ID has 20 digits (most significant bit set to 1), it fails to parse. But if it has 19, it doesn't fail, probably because it fits in a int64_t.

Is this a bug as well? The easiest fix for me would be to truncate the value, I guess there would be no problems with conflicts even when losing a bit.

Wait, max value of uint64_t is 9223372036854775807 (19 digits), so when you try to parse 20 digits number, output should be in a string haha

sndth avatar Sep 26 '25 23:09 sndth

It's that value times 2:

Image

you can see in the print the most significant bit is not set.

Edit: Ah, it doesn't necessarily need to have 20 digits. Just be greater than 2^63 (which means the first digit is set).

MirrasHue avatar Sep 26 '25 23:09 MirrasHue

Looking at the source code, I see the problem. Internally, get_value<uint64_t>() calls as_int():

struct from_node_int_helper {
    /// @brief Convert node's integer value to the target integer type.
    /// @param n A node object.
    /// @return An integer value converted from the node's integer value.
    static IntType convert(const BasicNodeType& n) {
        return n.as_int();
    }
};

I tried to implement to_node/from_node taking a uint64 and converting it to string and back to an integer with stoull(), but it didn't work.

One approach that worked was this one:

if(entity["ID"] != nullptr)
{
    if(entity["ID"].is_integer())
        id = entity["ID"].get_value<uint64>();
    else
        id = std::stoull(entity["ID"].get_value<std::string>());
}

MirrasHue avatar Sep 27 '25 00:09 MirrasHue