www.digitalmars.com         C & C++   DMDScript  

digitalmars.D - toChars buffer access?

reply Steven Schveighoffer <schveiguy gmail.com> writes:
I was about to reach for core.internal.string.signedToTempString [1] to 
convert a number into a temp string yet again. And I thought, maybe 
there's something in Phobos by now.

And lo and behold! There's std.conv.toChars [2]

But... it just gives me a range of char, and not an actual string. So my 
intended purpose (to use it as a string key for an associative array) 
means I have to copy it to a local buffer anyway.

No matter, right? It probably just stores the number and gives me access 
to each character as it parses. Oh wait, no. It actually stores it as a 
char[20], but doesn't give me access.

Which leaves me with the only option of doing:

auto s = someNumber.toChars;
char[20] buf;
import std.algorithm : copy;
copy(s.save, buf[0 .. s.length]);

auto v = aa[buf[0 .. s.length]];

But my goodness, it would just be so easy if it gave access to the buffer:

auto s = someNumber.toChars;
auto v = aa[s.data]; //  system access to buffer, unsafe!

And this is discounting the fact that I not only am copying the buffer 
*again*, but doing it one character at a time.

So would it be a problem to allow this accessor, even if it's  system? 
Or should I just keep importing core.internal?

-Steve

[1] 
https://github.com/dlang/druntime/blob/9f0f6e49fb379841c7934b6049f87dab0adfe53c/src/core/internal/string.d#L113

[2] https://dlang.org/phobos/std_conv.html#toChars
Aug 23 2020
parent reply Salih Dincer <salihdb hotmail.com> writes:
On Sunday, 23 August 2020 at 19:18:51 UTC, Steven Schveighoffer 
wrote:
 I was about to reach for 
 core.internal.string.signedToTempString [1] to convert a number 
 into a temp string yet again. And I thought, maybe there's 
 something in Phobos by now.

 And lo and behold! There's std.conv.toChars [2]

 But... it just gives me a range of char, and not an actual 
 string. So my intended purpose (to use it as a string key for 
 an associative array) means I have to copy it to a local buffer 
 anyway.

 No matter, right? It probably just stores the number and gives 
 me access to each character as it parses. Oh wait, no. It 
 actually stores it as a char[20], but doesn't give me access.

 Which leaves me with the only option of doing:
 ```d
 auto s = someNumber.toChars;
 char[20] buf;
 import std.algorithm : copy;
 copy(s.save, buf[0 .. s.length]);

 auto v = aa[buf[0 .. s.length]];
 ```
 But my goodness, it would just be so easy if it gave access to 
 the buffer:
 ```d
 auto s = someNumber.toChars;
 auto v = aa[s.data]; //  system access to buffer, unsafe!
 ```
 And this is discounting the fact that I not only am copying the 
 buffer *again*, but doing it one character at a time.

 So would it be a problem to allow this accessor, even if it's 
  system? Or should I just keep importing core.internal?

 -Steve

 [1] 
 https://github.com/dlang/druntime/blob/9f0f6e49fb379841c7934b6049f87dab0adfe53c/src/core/internal/string.d#L113

 [2] https://dlang.org/phobos/std_conv.html#toChars
I also faced this problem. Anyone have a solution? SDB 79
Jun 09 2022
parent reply bauss <jj_1337 live.dk> writes:
On Thursday, 9 June 2022 at 14:44:55 UTC, Salih Dincer wrote:
 On Sunday, 23 August 2020 at 19:18:51 UTC, Steven Schveighoffer 
 wrote:
 I was about to reach for 
 core.internal.string.signedToTempString [1] to convert a 
 number into a temp string yet again. And I thought, maybe 
 there's something in Phobos by now.

 And lo and behold! There's std.conv.toChars [2]

 But... it just gives me a range of char, and not an actual 
 string. So my intended purpose (to use it as a string key for 
 an associative array) means I have to copy it to a local 
 buffer anyway.

 No matter, right? It probably just stores the number and gives 
 me access to each character as it parses. Oh wait, no. It 
 actually stores it as a char[20], but doesn't give me access.

 Which leaves me with the only option of doing:
 ```d
 auto s = someNumber.toChars;
 char[20] buf;
 import std.algorithm : copy;
 copy(s.save, buf[0 .. s.length]);

 auto v = aa[buf[0 .. s.length]];
 ```
 But my goodness, it would just be so easy if it gave access to 
 the buffer:
 ```d
 auto s = someNumber.toChars;
 auto v = aa[s.data]; //  system access to buffer, unsafe!
 ```
 And this is discounting the fact that I not only am copying 
 the buffer *again*, but doing it one character at a time.

 So would it be a problem to allow this accessor, even if it's 
  system? Or should I just keep importing core.internal?

 -Steve

 [1] 
 https://github.com/dlang/druntime/blob/9f0f6e49fb379841c7934b6049f87dab0adfe53c/src/core/internal/string.d#L113

 [2] https://dlang.org/phobos/std_conv.html#toChars
I also faced this problem. Anyone have a solution? SDB 79
.array on the result from .toChars perhaps? Or is there a specific reason that cannot be done? ```d someNumber.toChars.array ```
Jun 09 2022
parent reply Steven Schveighoffer <schveiguy gmail.com> writes:
On 6/9/22 4:31 PM, bauss wrote:
 
 .array on the result from .toChars perhaps?
 
 Or is there a specific reason that cannot be done?
 
 ```d
 someNumber.toChars.array
 ```
This allocates. For a temporary array, this is wasteful (it will be thrown away immediately). My original example is to look up a key in an AA (not to store one). -Steve
Jun 09 2022
parent reply Salih Dincer <salihdb hotmail.com> writes:
On Thursday, 9 June 2022 at 21:16:48 UTC, Steven Schveighoffer 
wrote:
 My original example is to look up a key in an AA (not to store 
 one).
I have a simple code that works perfectly as well. But I'm having problems to convert except string. ```d struct HexStack { private { string[ubyte] convert; ubyte index; const ubyte first; } this(ubyte start) { this.index = start; this.first = start; } bool empty() { return index == first; } property top() { return this.convert[first]; } property pop() { if(!empty) --this.index; return this.convert[this.index]; } property push(uint data) { import std.conv; this.convert[index] = toChars!(16, char, LetterCase.upper) (data).to!string; if(index < ubyte.max) this.index++; else this.index = ubyte.max; } } unittest { HexStack foo = 'a'; // x'61' foo.push('A'); // x'41' assert(foo.top == "41"); foo.push('B'); foo.push('C'); assert(foo.pop == "43"); assert(foo.pop == "42"); assert(foo.pop == "41"); assert(foo.empty); assert(foo.pop == "41"); assert(foo.pop == foo.top); HexStack bar = ubyte.max - 1; bar.push('A'); // x'41' assert(bar.top == "41"); bar.push('B'); bar.push('C'); assert(bar.pop == "41"); assert(bar.empty); assert(bar.pop == "41"); assert(bar.pop == bar.top); } ``` So hexstring is ok. When switching to other types, it converts each letter independently of each other and gives the following error:
 ChArray(Reserved, T = char)
neme1234567 den eme 9,8,7,6,5,4,3,2,1,e,m,e,n,e,d, d,e,n,e,m,e,1,2,3,4,5,6,7,8,9, std.conv.ConvException /usr/src/dmd/linux/bin64/../../src/phob s/std/conv.d(1877): Unexpected '0' when converting from type Result to type char ---------------- ```d ??:? pure safe char std.conv.toImpl!(char, std.conv.toChars!(16u, char, 0, uint).toChars(uint).Result).toImpl(std.conv.toChars!(16u, char, 0, uint).toChars(uint).Result) [0x55a583a8c4de] ??:? pure safe char std.conv.to!(char).to!(std.conv.toChars!(16u, char, 0, uint).toChars(uint).Result).to(std.conv.toChars!(16u, char, 0, uint).toChars(uint).Result) [0x55a583a8c201] ??:? pure char (immutable(char)[], char).ChArray.toChar!(10u).toChar() [0x55a583a8bf01] ??:? _Dmain [0x55a583a8018d] Process finished with exit code 1. ``` I also get this error when I use char[]:
 ChArray.d(180): Error: only one index allowed to index `char`
**Test Code:** ```d uint A = 65; auto result = A.toChars!16; //hex '41' auto myCodePage = HexStack('a'); myCodePage.push(A); assert(myCodePage.top == result.to!string); auto test = result.to!char[]; ``` The only solution is to use .array: ```d assert(result.array == ['4', '1']); ``` But I don't like this! SDB 79
Jun 09 2022
parent reply Steven Schveighoffer <schveiguy gmail.com> writes:
On 6/9/22 8:18 PM, Salih Dincer wrote:
    auto test = result.to!char[];
I'm not sure if I got all your code, but just to note, the operator precedence here means it's `(result.to!char)[]`
 The only solution is to use .array:
 
 assert(result.array == ['4', '1']);
Well, 2 things. First, you can compare a char[] to a string, so `result.array == "41"` should be fine. Second, you can't use == to compare ranges, you need to use std.algorithm.equal: ```d import std.algorithm : equal; assert(equal(result, "41")); ``` (and yes, this should work for different character types, even though a string is element type dchar). Not sure if this helps, I feel like there was some code missing from your example. -Steve
Jun 09 2022
parent reply Salih Dincer <salihdb hotmail.com> writes:
On Friday, 10 June 2022 at 01:49:25 UTC, Steven Schveighoffer 
wrote:
 On 6/9/22 8:18 PM, Salih Dincer wrote:
    auto test = result.to!char[];
I'm not sure if I got all your code, but just to note, the operator precedence here means it's `(result.to!char)[]` [...]
It's okay, the subject is clearer for me now. But I wish I could change each character to hexString without dealing with conversions; I see below is the type of integer(uint) that toChars wants: ```d void main() { import std.algorithm, std.range; import std.conv, std.stdio; string str = "Hello World!"; /* I'd like it to be as simple as a * comment line, but it's not working. */ //str.toChars!(16, string).writeln;/* str.each!(c => c.to!uint .toChars!(16, char, LetterCase.upper) .write("-") ); writeln; str.each!((i, uint c) { c.to!string(16) .writeln(": ", i); } );//*/ } /* Print Out: 48-65-6C-6C-6F-20-57-6F-72-6C-64-21- 48: 0 65: 1 6C: 2 6C: 3 6F: 4 20: 5 57: 6 6F: 7 72: 8 6C: 9 64: 10 21: 11 //*/ ``` SDB 79
Jun 09 2022
parent reply Steven Schveighoffer <schveiguy gmail.com> writes:
On 6/10/22 1:07 AM, Salih Dincer wrote:
    /* I'd like it to be as simple as a
     * comment line, but it's not working.
     */
    //str.toChars!(16, string).writeln;/*
    str.each!(c =>
      c.to!uint
       .toChars!(16, char, LetterCase.upper)
       .write("-")
    );
Ugh, it's not as easy as I thought but... ```d import std.utf; // option 1 (if you want a range that's usable elsewhere) str.map!(c => uint(c).toChars!(16, char, LetterCase.upper)) .joiner("-".byCodeUnit) // byCodeUnit needed because of autodecoding .writeln; // option 2 (if the point is just to print) writefln("%-(%02X-%)", str); ``` -Steve
Jun 10 2022
parent reply Salih Dincer <salihdb hotmail.com> writes:
On Friday, 10 June 2022 at 14:49:51 UTC, Steven Schveighoffer 
wrote:
   // option 2 (if the point is just to print)
   writefln("%-(%02X-%)", str);
 ```
Delicious! ```d auto str = "Hello World!"; import std.format, std.range : split; auto strArr = str.format!"%-(%02X %)".split; import std.stdio; strArr.writefln!"%-(%s-%)"; ``` SDB 79
Jun 10 2022
parent Steven Schveighoffer <schveiguy gmail.com> writes:
On 6/10/22 5:05 PM, Salih Dincer wrote:
 On Friday, 10 June 2022 at 14:49:51 UTC, Steven Schveighoffer wrote:
   // option 2 (if the point is just to print)
   writefln("%-(%02X-%)", str);
 ```
Delicious! ```d   auto str = "Hello World!";   import std.format, std.range : split;   auto strArr = str.format!"%-(%02X %)".split;
This allocates ;) -Steve
Jun 10 2022