Thread overview
D: Convert/parse uint integer to string. (@nogc)
Nov 24, 2023
BoQsc
Nov 24, 2023
Ferhat Kurtulmuş
Nov 30, 2023
kdevel
Nov 27, 2023
Nick Treleaven
Nov 27, 2023
Nick Treleaven
Nov 28, 2023
Mark Davies
Nov 28, 2023
Dom DiSc
Nov 30, 2023
kdevel
Nov 28, 2023
Julian Fondren
Nov 30, 2023
Siarhei Siamashka
November 24, 2023

I tried to look into https://dlang.org/phobos/std_conv.html

Most of the functions inside std.conv seem to be dependant on Garbage Collection.

And I couldn't find a straightforward way to produce a string value out of uint value.

How to convert or parse uint value to a string in @nogc way?

November 24, 2023

On Friday, 24 November 2023 at 09:35:00 UTC, BoQsc wrote:

>

I tried to look into https://dlang.org/phobos/std_conv.html

Most of the functions inside std.conv seem to be dependant on Garbage Collection.

And I couldn't find a straightforward way to produce a string value out of uint value.

How to convert or parse uint value to a string in @nogc way?

I guess there are third-party libraries doing this. One would use stdc functions such as sprintf. Probably, there should be a more d-ish way.

import core.stdc.stdio : sprintf;
import core.stdc.math : log10;

import std.exception : assumeUnique;
import std.stdio : writeln;

    size_t nod(int num){
      return cast(size_t)((num==0)?1:log10(num)+1);
    }

    void main()
    {
       int myint = 23;

       char[80] str;

       sprintf(str.ptr, "%d", myint);

       string _dstring = str[0..nod(myint)].assumeUnique;

       writeln(_dstring);

}
November 27, 2023

On Friday, 24 November 2023 at 09:35:00 UTC, BoQsc wrote:

>

I tried to look into https://dlang.org/phobos/std_conv.html

Most of the functions inside std.conv seem to be dependant on Garbage Collection.

And I couldn't find a straightforward way to produce a string value out of uint value.

How to convert or parse uint value to a string in @nogc way?

You can use std.conv.toChars:

void main() @nogc
{
    int n = 515;

    import std.conv;
    char[10] s = 0;
    auto r = n.toChars();
    assert(r.length < s.length);
    size_t i;
    foreach (c; r)
        s[i++] = c;

    import core.stdc.stdio;
    puts(s.ptr);
}
November 27, 2023

On Monday, 27 November 2023 at 12:34:30 UTC, Nick Treleaven wrote:

>

On Friday, 24 November 2023 at 09:35:00 UTC, BoQsc wrote:
You can use std.conv.toChars:

void main() @nogc
{
    int n = 515;

    import std.conv;
    char[10] s = 0;
    auto r = n.toChars();
    assert(r.length < s.length);
    size_t i;
    foreach (c; r)
        s[i++] = c;

    import core.stdc.stdio;
    puts(s.ptr);
}

Or, using std.experimental.allocator:

void main() @nogc
{
    int n = 515;

    import std.conv;
    import std.experimental.allocator;
    import std.experimental.allocator.mallocator;
    alias a = Mallocator.instance;
    auto s = a.makeArray(n.toChars);
    assert(s == "515");
    a.dispose(s);
}
November 28, 2023

On Friday, 24 November 2023 at 09:35:00 UTC, BoQsc wrote:

>

I tried to look into https://dlang.org/phobos/std_conv.html

Most of the functions inside std.conv seem to be dependant on Garbage Collection.

And I couldn't find a straightforward way to produce a string value out of uint value.

How to convert or parse uint value to a string in @nogc way?

I did it this way ...

import std.stdio;

char[10] longToString(long n) @nogc
{
	char[10] x;
	ulong power;
	
	x[0] = '-'*(n<0);

	long t = (n<0)*-n + (n>0)*n ;

	while (n != 0)
	{
		power++;
		n /= 10;
	}
	
	power -= (x[0] != '-');
	
	while (t > 0)
	{
		x[power] = (t % 10) + '0';
		power--;
		t /= 10;
	}
	
	return x;
}


int main()
{
	long p = 12345;
	char[10] r = longToString(p);
	writeln(r);
	
	p = -12345;
	r = longToString(p);
	writeln(r);
	
	return 0;
}
November 28, 2023

On Tuesday, 28 November 2023 at 08:51:21 UTC, Mark Davies wrote:

>

On Friday, 24 November 2023 at 09:35:00 UTC, BoQsc wrote:

import std.stdio;

char[10] longToString(long n) @nogc

For a 'long' 10 characters is likely to be not enough (long max is 9223372036854775808 which has 19 chars, and you should reserve additional one for the sign and one for the terminating null), so I would at least recommend using char[21].

with char[10] your function becomes a big hole in your security, as it can easily be misused to write 10 bytes of freely selectable garbage behind your allocated memory.

But as you want to avoid the gc, security might not be a goal for you, so continue living in the 1970's.

November 28, 2023

On Tuesday, 28 November 2023 at 08:51:21 UTC, Mark Davies wrote:

>

I did it this way ...

You always print the full array of bytes this way. Output piped to od -c is

0000000   1   2   3   4   5 377 377 377 377 377  \n   -   1   2   3   4
0000020   5 377 377 377 377  \n

Those 377s are char.init, 0xFF.

On Tuesday, 28 November 2023 at 09:43:47 UTC, Dom DiSc wrote:

>

For a 'long' 10 characters is likely to be not enough (long max is 9223372036854775808 which has 19 chars, and you should reserve additional one for the sign and one for the terminating null), so I would at least recommend using char[21].

Signed max is all bits but the sign bit set, so 7FFF...FFFF, so signed max is always an odd number. d can confirm:

$ rdmd --eval 'writeln(long.max)'
9223372036854775807
$ rdmd --eval 'writeln(2+to!string(long.max).length)'
21

There's no terminating NUL here, which might be an oversight.

>

with char[10] your function becomes a big hole in your security, as it can easily be misused to write 10 bytes of freely selectable garbage behind your allocated memory.

This is D though, so without separately disabling bounds checks, there's an error:

core.exception.ArrayIndexError@d1.d(22): index [18] is out of bounds for array of length 10

or with -betterC:

d1: d1.d:21: Assertion `array index out of bounds' failed.
Aborted

Here's a minimal edit to fix the char.init output:

char[] longToString(long n) @nogc
{
	static char[21] x;
	size_t length;
	ulong power;
	
	x[0] = '-'*(n<0);

	long t = (n<0)*-n + (n>0)*n ;

	while (n != 0)
	{
		power++;
		n /= 10;
	}
	
	length = power;
	power -= (x[0] != '-');
	
	while (t > 0)
	{
		x[power] = (t % 10) + '0';
		power--;
		t /= 10;
	}
	
	return x[0 .. length];
}

As you can see, slices from this longToString are good until the next call to it. The other C-like way to manage memory is to pass in the buffer to use, which in D can be a slice of a static array on the caller's stack. You'll probably have a much better time with manual memory management if you use custom allocators.

November 30, 2023

On Friday, 24 November 2023 at 13:05:30 UTC, Ferhat Kurtulmuş wrote:

>

[...]

import core.stdc.stdio : sprintf;
import core.stdc.math : log10;

import std.exception : assumeUnique;
import std.stdio : writeln;

    size_t nod(int num){
      return cast(size_t)((num==0)?1:log10(num)+1);
    }

    void main()
    {
       int myint = 23;

       char[80] str;

       sprintf(str.ptr, "%d", myint);

       string _dstring = str[0..nod(myint)].assumeUnique;

       writeln(_dstring);

}

What happend to the indentation? Anyway

$ ./inttostr -1

$ ./inttostr -2147483648

I like snprintf:

import core.stdc.stdio : snprintf;
import std.exception : assumeUnique, enforce;
import std.stdio;
import std.conv;
import core.stdc.locale;
import std.format;

void main (string [] args)
{
   setlocale (LC_NUMERIC, "");
   auto myint = args[1].to!long;
   char[27] str;
   auto n = snprintf(str.ptr, str.sizeof, "%'ld", myint);
   enforce (n < str.sizeof, "buffer too small");
   string _dstring = str[0..n].assumeUnique;
   writeln(_dstring);
}
$ ./l2s -9223372036854775808
-9.223.372.036.854.775.808
November 30, 2023

On Tuesday, 28 November 2023 at 09:43:47 UTC, Dom DiSc wrote:

>

On Tuesday, 28 November 2023 at 08:51:21 UTC, Mark Davies wrote:

>

On Friday, 24 November 2023 at 09:35:00 UTC, BoQsc wrote:

import std.stdio;

char[10] longToString(long n) @nogc

For a 'long' 10 characters is likely to be not enough (long max is 9223372036854775808 [...]

long.max is 9223372036854775807, long.min is -9223372036854775808. Besides from the too short buffer the longToString function neither converts 0 (zero) nor long.min correctly.

November 30, 2023

On Friday, 24 November 2023 at 09:35:00 UTC, BoQsc wrote:

>

I tried to look into https://dlang.org/phobos/std_conv.html

Most of the functions inside std.conv seem to be dependant on Garbage Collection.

And I couldn't find a straightforward way to produce a string value out of uint value.

How to convert or parse uint value to a string in @nogc way?

There are various tricks to do such conversion very fast. For example, the following article is essential for implementing it: https://lemire.me/blog/2021/06/03/computing-the-number-of-digits-of-an-integer-even-faster/

Rather than doing conversion one digit at a time, it's much more efficient to process multiple digits per loop iteration. Consider an illustrative pseudocode like this:

    while (x >= 100) {
      write(twodigits_lookup_table[x % 100]); // we can handle two digits at once
      x /= 100;
    }

I have a @nogc compatible DUB module, which implements fast formatted output to stdout: https://github.com/ssvb/speedy-stdio (cutting some corners allows to squeeze some extra performance out of it).

The same code can be adapted to do formatted output to a memory buffer as well.