Thread overview
Simple way to handle rvalues and templates.
Feb 27, 2022
Chris Piker
Feb 27, 2022
Ali Çehreli
Feb 27, 2022
Salih Dincer
February 27, 2022

Hi D

I have bit of code that was tripping me up. I need to parse small fields out of a big binary read, and it looks like some operations just can't be composed when it comes to
using templates. So this works:

import std.bitmanip, std.system;
ubyte[8192] data;

ubyte[] temp = data[4..6];
ushort us = temp.read!(ushort, Endian.bigEndian);
// intentionally provided the default byte order for readability

But this doesn't work:

import std.bitmanip, std.system;
ubyte[8192] data;

ushort us = data[4..6].read!(ushort, Endian.bigEndian);

The reasons for this are probably old hat for seasoned D programmers by this is really confusing for newbies.

Is there a better way to handle this instead of making a bunch of temporary variables that I don't care about? Matlab has this behavior too, statements that should be composable aren't, and it drives me crazy since Java and Python don't seem to suffer from this problem near a much.

February 26, 2022
On 2/26/22 19:38, Chris Piker wrote:

> But this doesn't work:
> ```d
> import std.bitmanip, std.system;
> ubyte[8192] data;
>
> ushort us = data[4..6].read!(ushort, Endian.bigEndian);
> ```
> The reasons for this are probably old hat for seasoned D programmers by
> this is really confusing for newbies.

Agreed. read() is designed to advance the buffer that it is given. It is thought to be convenient for the next read() operation because the semantics would be "read a ushort and then read an int", etc. And the buffer would be consumed by read().

To play the way read() wants us to play, the first thing that comes to mind is to read original data into a storage and define a range (data below) for read() to consume.

However, it requires skipping over bytes by e.g. dropExactly(), which is presumably a very fast operation for slices:

void main() {
  import std.bitmanip, std.system;
  ubyte[8192] storage;
  auto data = storage[];

  import std.range;
  data.dropExactly(4);

  ushort us = data.read!(ushort, Endian.bigEndian);
  // Here, data is ready for the next read.
}

Another option is to write a wrapper which takes your slice by copy so that read() is happy as it consumes a parameter instead of your rvalue:

import std.system;

// Adapting read()'s interface with one difference: 'ref' is removed:

auto readFrom(T, Endian endianness = Endian.bigEndian, R)(R range) {
  import std.bitmanip;
  return range.read!(ushort, endianness);
}

void main() {
  import std.bitmanip, std.system;
  ubyte[8192] data;

  ushort us = data[4..6].readFrom!(ushort, Endian.bigEndian);
}

I don't like the name readFrom() yet but that works. :)

Ali

February 27, 2022

On Sunday, 27 February 2022 at 06:11:28 UTC, Ali Çehreli wrote:

>

I don't like the name readFrom() yet but that works. :)

It seems very delicious, can stay as read():

auto read(T, Endian E = Endian.bigEndian, R)
             (R range) {
  import bop = std.bitmanip;
  return bop.read!(T, E)(range);
}

void main() {
  import std.system;

  ubyte[8] d = [ 0xFF, 0xFF, 0xFF, 0xFF,
                 0xAA, 0xAA, 0xFF, 0xFF ];

  ushort us = d[4..6].read!ushort;
  assert(us == 0xAAAA);
}

SDB79