Weird bug in std.logger? Possible memory corruption

2023-10-31 Thread Arafel via Digitalmars-d-learn

Hi,

Today I have just found a weird bug in std.logger. Consider:

```d
import std.logger : info;

void main() {
info(foo());
}

auto foo() {
info("In foo");
return "Hello, world.";
}
```

The output is:

```
2023-10-31T20:41:05.274 [info] onlineapp.d:8:foo In foo
2023-10-31T20:41:05.274 [info] onlineapp.d:8:foo In fooHello, world.
```

The second line is obviously wrong, as it repeats the first line as its 
header. That's why I suspect memory corruption.


Assigning the value to a variable works as expected:

```d
import std.logger : info;

void main() {
auto s = foo();
info(s);
}

auto foo() {
info("In foo");
return "Hello, world.";
}
```

gets the proper output:

```
2023-10-31T21:09:46.529 [info] onlineapp.d:9:foo In foo
2023-10-31T21:09:46.529 [info] onlineapp.d:5:main Hello, world.
```

I can only imagine that it's related to the logging functions taking 
lazy arguments, although I cannot see why it would be a problem in a 
simple case like this.


Function Overloading

2023-10-31 Thread Salih Dincer via Digitalmars-d-learn

```d
struct Calculate
{
    int memory;
string result;

    auto toString() => result;

    this(string str)
    {
        add(str);
    }

    this(int num)
{
        add(num);
}

import std.string : format;

    void add(string str)
    {
        result ~= str.format!"%s + ";
    }

void add(int num)
    {
        memory += num;
        add(num.format!"%s");
    }
}

import std.stdio;
void main()
{
    // without constructor:
    Calculate c;
    c.add("x");
    c.add(2);
    c.writeln; // x + 2 +
    
    // with constructor:
    c = Calculate("x");
c.add(2);
    c.writeln; // x + 2 +
}
```

There is a simple struct like the one above that is not related 
to reality. There can be more than 2 function overloading in it, 
but in our example there are only 2. Without implementing a 
similar function again for each constructor; is it possible to do 
something like `alias add = this;`?


```d
struct Calculate
{
    int memory;
string result;

    auto toString() => result;

    // alias add = this;
    
import std.string : format;

    this(string str)
    {
        result ~= str.format!"%s + ";
    }

this(int num)
    {
        memory += num;
        add(num.format!"%s");
    }
}
```

SDB@79


Re: bigEndian in std.bitmanip

2023-10-31 Thread Jonathan M Davis via Digitalmars-d-learn
On Tuesday, October 31, 2023 8:23:28 AM MDT Salih Dincer via Digitalmars-d-
learn wrote:
> On Tuesday, 31 October 2023 at 10:24:56 UTC, Jonathan M Davis
>
> wrote:
> > On Tuesday, October 31, 2023 4:09:53 AM MDT Salih Dincer via
> >
> > Digitalmars-d- learn wrote:
> >> Hello,
> >>
> >> Why isn't Endian.littleEndian the default setting for read() in
> >> std.bitmanip?
> >
> > Why would you expect little endian to be the default? The
> > typical thing to do when encoding integral values in a
> > platform-agnostic manner is to use big endian, not little
> > endian...
>
> Because when we create a structure with a Union, it does reverse
> insertion with according to the static array(bytes) index; I
> showed this above.

I fail to see what the situation with the union has to do with anything.
Sure, you can convert between an array of bytes and an int with a union if
you want to, but what that does is going to be dependent on your local
architecture. read and its related functions in std.bitmanip are
architecture-independent. So, they will convert from little endian or big
endian regardless of what your local architecture is. You would typically
use it on ranges of bytes that come from the network or from serialized
data. The most common scenario there is likely to be that they'll be in big
endian, because that's what platforma-independent binary formats typically
do, but you can explicitly tell read that the range is in little endian if
your range of bytes happens to be in little endian. Both scenarios can
occur, and it supports both. It just defaults to big endian, because that's
the more common scenario when dealing with binary formats.

> I also have a convenience template like this:
> ```d
> template readBytes(T, bool big = false, R)
> {// pair endian version 2.0
>import bop = std.bitmanip;
>
>static if(big)
>  enum E = bop.Endian.bigEndian;
>else
>  enum E = bop.Endian.littleEndian;
>
>auto readBytes(ref R dat)
> => bop.read!(T, E)(dat);
> }
> ```
> Sorry to give you extra engage because I already solved the
> problem with readBytes(). Thank you for your answer, but there is
> 1 more problem, or even 2! The read() in the library, which is
> 2nd function, conflicts with std.write. Yeah, there are many
> solutions to this, but what it does is just read bytes. However,
> you can insert 4 ushorts into one ulong.
>
> Don't you think the name of the function should be readBytes, not
> read?  Because it doesn't work with any type other than ubyte[]!

D's module system makes it so that names do not need to be unique across
modules, and this is not the only case in Phobos where multiple modules use
the same function name. It's easy enough to import only the functions you're
using or to rename them via the import if you happen to be importing from
multiple modules containing functions with the same name. E.G. if you want
to do

std.bitmanip : readBytes = read;

then you can.

- Jonathan M Davis





Re: bigEndian in std.bitmanip

2023-10-31 Thread Imperatorn via Digitalmars-d-learn

On Tuesday, 31 October 2023 at 10:09:53 UTC, Salih Dincer wrote:

Hello,

Why isn't Endian.littleEndian the default setting for read() in 
std.bitmanip?


Okay, we can easily change this if we want (I could use enum LE 
in the example) and I can also be reversed with 
data.retro.array().


```d
void main()
{
  import std.conv : hexString;
  string helloD = hexString!"48656C6C6F204421";
  // compile time converted literal string -ˆ

  import std.string : format;
  auto hexF = helloD.format!"%(%02X%)";

  import std.digest: toHexString;
  auto arr = cast(ubyte[])"Hello D!";

  auto hex = arr.toHexString;
  assert(hex == hexF);

  import std.stdio : writeln;
  hex.writeln(": ", helloD);
// 48656C6C6F204421: Hello D!
  assert(helloD == "Hello D!");

  auto data = arr.readBytes!size_t;
  data.code.writeln(": ", data.bytes);
// 2397076564600448328: Hello D!
}

template readBytes(T, R)
{
  union Bytes
  {
T code;
char[T.sizeof] bytes;
  }
  import std.bitmanip;
  enum LE = Endian.littleEndian;

  auto readBytes(ref R data)
  {
   import std.range : retro, array;
   auto reverse = data.retro.array;
   return Bytes(reverse.read!T);
  }
}
```

However, I think it is not compatible with Union. Thanks...

SDB@79


It might make sense to change since little endian is the most 
common when it comes to hardware. But big endian is most common 
when it comes to networking. So I guess it depends on your view 
of what is most common. Interacting with your local hardware or 
networking.


Re: bigEndian in std.bitmanip

2023-10-31 Thread Salih Dincer via Digitalmars-d-learn
On Tuesday, 31 October 2023 at 10:24:56 UTC, Jonathan M Davis 
wrote:
On Tuesday, October 31, 2023 4:09:53 AM MDT Salih Dincer via 
Digitalmars-d- learn wrote:

Hello,

Why isn't Endian.littleEndian the default setting for read() in
std.bitmanip?


Why would you expect little endian to be the default? The 
typical thing to do when encoding integral values in a 
platform-agnostic manner is to use big endian, not little 
endian...


Because when we create a structure with a Union, it does reverse 
insertion with according to the static array(bytes) index; I 
showed this above.  I also have a convenience template like this:


```d
template readBytes(T, bool big = false, R)
{// pair endian version 2.0
  import bop = std.bitmanip;

  static if(big)
enum E = bop.Endian.bigEndian;
  else
enum E = bop.Endian.littleEndian;

  auto readBytes(ref R dat)
   => bop.read!(T, E)(dat);
}
```
Sorry to give you extra engage because I already solved the 
problem with readBytes(). Thank you for your answer, but there is 
1 more problem, or even 2! The read() in the library, which is 
2nd function, conflicts with std.write. Yeah, there are many 
solutions to this, but what it does is just read bytes. However, 
you can insert 4 ushorts into one ulong.


Don't you think the name of the function should be readBytes, not 
read?  Because it doesn't work with any type other than ubyte[]!


SDB@79



Re: bigEndian in std.bitmanip

2023-10-31 Thread Jonathan M Davis via Digitalmars-d-learn
On Tuesday, October 31, 2023 4:09:53 AM MDT Salih Dincer via Digitalmars-d-
learn wrote:
> Hello,
>
> Why isn't Endian.littleEndian the default setting for read() in
> std.bitmanip?

Why would you expect little endian to be the default? The typical thing to
do when encoding integral values in a platform-agnostic manner is to use big
endian, not little endian. Either way, it supports both big endian and
little endian, so if your use case requires little endian, you can do that.
You just have to specifiy the endianness, and if you find that to be too
verbose, you can create a wrapper to use in your own code.

- Jonathan M Davis





bigEndian in std.bitmanip

2023-10-31 Thread Salih Dincer via Digitalmars-d-learn

Hello,

Why isn't Endian.littleEndian the default setting for read() in 
std.bitmanip?


Okay, we can easily change this if we want (I could use enum LE 
in the example) and I can also be reversed with 
data.retro.array().


```d
void main()
{
  import std.conv : hexString;
  string helloD = hexString!"48656C6C6F204421";
  // compile time converted literal string -ˆ

  import std.string : format;
  auto hexF = helloD.format!"%(%02X%)";

  import std.digest: toHexString;
  auto arr = cast(ubyte[])"Hello D!";

  auto hex = arr.toHexString;
  assert(hex == hexF);

  import std.stdio : writeln;
  hex.writeln(": ", helloD);
// 48656C6C6F204421: Hello D!
  assert(helloD == "Hello D!");

  auto data = arr.readBytes!size_t;
  data.code.writeln(": ", data.bytes);
// 2397076564600448328: Hello D!
}

template readBytes(T, R)
{
  union Bytes
  {
T code;
char[T.sizeof] bytes;
  }
  import std.bitmanip;
  enum LE = Endian.littleEndian;

  auto readBytes(ref R data)
  {
   import std.range : retro, array;
   auto reverse = data.retro.array;
   return Bytes(reverse.read!T);
  }
}
```

However, I think it is not compatible with Union. Thanks...

SDB@79