www.digitalmars.com         C & C++   DMDScript  

digitalmars.D.learn - Initializing static array with contents of (static and dynamic)

reply Johannes Loher <johannesloher fg4f.de> writes:
In a project I am currently working on, I have lot's of code of the
following form:

static immutable ubyte[4] sigma0 = [101, 120, 112,  97];
static immutable ubyte[4] sigma1 = [110, 100,  32,  51];
static immutable ubyte[4] sigma2 = [ 50,  45,  98, 121];
static immutable ubyte[4] sigma3 = [116, 101,  32, 107];

void func(in ubyte[32] key, in ubyte[16] n)
{
    ubyte[64] buf;
    buf[0..4] = sigma0;
    buf[4..20] = key[0..16];
    buf[20..24] = sigma1;
    buf[24..40] = n;
    buf[40..44] = sigma2;
    buf[44..60] = key[16..$];
    buf[60..64] = sigma3;

   /* do something with buf */
}

This looks really bad to me. I would like to initialize buf with the
corresponding values (the way it's done now, it is impossible to make
buf immutable...).

One option for this would be to use ~ to concatenate the arrays. But
this obviously results in GC allocations, which I want to avoid here,
because the functions get called very often.

Another option would be to to manually expand the arrays and initialize
buf with that:

ubyte[64] buf = [sigma0[0], sigma0[1], /* ... */, sigma3[3]];

This is obviously very annoying and error prone.

Whe searching for a solution, I found this thread about automatic
expanding of arrays:

http://forum.dlang.org/thread/hwellpcaomwbpnpofzlx forum.dlang.org?page=1

This would result in the following code for me:

ubyte[64] buf = [
    expand!sigma0,
    expand!key[0..16],
    expand!sigma1,
    expand!n,
    expand!sigma2,
    expand!key[16..$],
    expand!sigma3
];

Is this the suggested solution (is it robust)? Is there anything like
this in Phobos?

Thanks for your help!
Jul 04 2016
next sibling parent reply ZombineDev <petar.p.kirov gmail.com> writes:
On Monday, 4 July 2016 at 14:31:41 UTC, Johannes Loher wrote:
 In a project I am currently working on, I have lot's of code of 
 the following form:

 static immutable ubyte[4] sigma0 = [101, 120, 112,  97]; static 
 immutable ubyte[4] sigma1 = [110, 100,  32,  51]; static 
 immutable ubyte[4] sigma2 = [ 50,  45,  98, 121]; static 
 immutable ubyte[4] sigma3 = [116, 101,  32, 107];

 void func(in ubyte[32] key, in ubyte[16] n)
 {
     ubyte[64] buf;
     buf[0..4] = sigma0;
     buf[4..20] = key[0..16];
     buf[20..24] = sigma1;
     buf[24..40] = n;
     buf[40..44] = sigma2;
     buf[44..60] = key[16..$];
     buf[60..64] = sigma3;

    /* do something with buf */
 }

 This looks really bad to me. I would like to initialize buf 
 with the corresponding values (the way it's done now, it is 
 impossible to make buf immutable...).

 One option for this would be to use ~ to concatenate the 
 arrays. But this obviously results in GC allocations, which I 
 want to avoid here, because the functions get called very often.

 Another option would be to to manually expand the arrays and 
 initialize buf with that:

 ubyte[64] buf = [sigma0[0], sigma0[1], /* ... */, sigma3[3]];

 This is obviously very annoying and error prone.

 Whe searching for a solution, I found this thread about 
 automatic expanding of arrays:

 http://forum.dlang.org/thread/hwellpcaomwbpnpofzlx forum.dlang.org?page=1

 This would result in the following code for me:

 ubyte[64] buf = [
     expand!sigma0,
     expand!key[0..16],
     expand!sigma1,
     expand!n,
     expand!sigma2,
     expand!key[16..$],
     expand!sigma3
 ];

 Is this the suggested solution (is it robust)? Is there 
 anything like
 this in Phobos?

 Thanks for your help!
You should be able to use http://dlang.org/phobos-prerelease/std_meta#aliasSeqOf for this. I think it should work with the same syntax as expand, but with added bonus that it also handles ranges like those in std.range and std.algorithm.
Jul 04 2016
parent Johannes Loher <johannesloher fg4f.de> writes:
Am 04.07.2016 um 19:24 schrieb ZombineDev:
 On Monday, 4 July 2016 at 14:31:41 UTC, Johannes Loher wrote:
 In a project I am currently working on, I have lot's of code of the
 following form:

 static immutable ubyte[4] sigma0 = [101, 120, 112,  97]; static
 immutable ubyte[4] sigma1 = [110, 100,  32,  51]; static immutable
 ubyte[4] sigma2 = [ 50,  45,  98, 121]; static immutable ubyte[4]
 sigma3 = [116, 101,  32, 107];

 void func(in ubyte[32] key, in ubyte[16] n)
 {
     ubyte[64] buf;
     buf[0..4] = sigma0;
     buf[4..20] = key[0..16];
     buf[20..24] = sigma1;
     buf[24..40] = n;
     buf[40..44] = sigma2;
     buf[44..60] = key[16..$];
     buf[60..64] = sigma3;

    /* do something with buf */
 }

 This looks really bad to me. I would like to initialize buf with the
 corresponding values (the way it's done now, it is impossible to make
 buf immutable...).

 One option for this would be to use ~ to concatenate the arrays. But
 this obviously results in GC allocations, which I want to avoid here,
 because the functions get called very often.

 Another option would be to to manually expand the arrays and
 initialize buf with that:

 ubyte[64] buf = [sigma0[0], sigma0[1], /* ... */, sigma3[3]];

 This is obviously very annoying and error prone.

 Whe searching for a solution, I found this thread about automatic
 expanding of arrays:

 http://forum.dlang.org/thread/hwellpcaomwbpnpofzlx forum.dlang.org?page=1

 This would result in the following code for me:

 ubyte[64] buf = [
     expand!sigma0,
     expand!key[0..16],
     expand!sigma1,
     expand!n,
     expand!sigma2,
     expand!key[16..$],
     expand!sigma3
 ];

 Is this the suggested solution (is it robust)? Is there anything like
 this in Phobos?

 Thanks for your help!
You should be able to use http://dlang.org/phobos-prerelease/std_meta#aliasSeqOf for this. I think it should work with the same syntax as expand, but with added bonus that it also handles ranges like those in std.range and std.algorithm.
I just tried that, but it does not work for arrays, which can not be read at compile time.
Jul 04 2016
prev sibling next sibling parent reply =?UTF-8?Q?Ali_=c3=87ehreli?= <acehreli yahoo.com> writes:
On 07/04/2016 07:31 AM, Johannes Loher wrote:
 In a project I am currently working on, I have lot's of code of the
 following form:

 static immutable ubyte[4] sigma0 = [101, 120, 112,  97];
 static immutable ubyte[4] sigma1 = [110, 100,  32,  51];
 static immutable ubyte[4] sigma2 = [ 50,  45,  98, 121];
 static immutable ubyte[4] sigma3 = [116, 101,  32, 107];

 void func(in ubyte[32] key, in ubyte[16] n)
 {
      ubyte[64] buf;
      buf[0..4] = sigma0;
      buf[4..20] = key[0..16];
      buf[20..24] = sigma1;
      buf[24..40] = n;
      buf[40..44] = sigma2;
      buf[44..60] = key[16..$];
      buf[60..64] = sigma3;

     /* do something with buf */
 }
Here's an option that overlays a struct on top of the bytes: struct Buf { union { struct { ubyte[4] sigma0 = [101, 120, 112, 97]; ubyte[16] keyBeg; ubyte[4] sigma1 = [110, 100, 32, 51]; ubyte[16] n; ubyte[4] sigma2 = [ 50, 45, 98, 121]; ubyte[16] keyEnd; ubyte[4] sigma3 = [116, 101, 32, 107]; } ubyte[64] bytes; } this(const ubyte[] key, const ubyte[] n) { this.keyBeg = key[0..16]; this.keyEnd = key[16..$]; this.n = n; } } static assert(Buf.sizeof == 64); void func(in ubyte[] key, in ubyte[] n) { auto buf = Buf(key, n); writeln(buf.bytes); } import std.stdio; import std.range; import std.array; void main() { func(ubyte(32).iota.array, ubyte(16).iota.array); } Prints: [101, 120, 112, 97, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 110, 100, 32, 51, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 50, 45, 98, 121, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, 116, 101, 32, 107] Ali
Jul 04 2016
parent reply Johannes Loher <johannesloher fg4f.de> writes:
Am 04.07.2016 um 20:33 schrieb Ali Çehreli:
 On 07/04/2016 07:31 AM, Johannes Loher wrote:
 In a project I am currently working on, I have lot's of code of the
 following form:

 static immutable ubyte[4] sigma0 = [101, 120, 112,  97];
 static immutable ubyte[4] sigma1 = [110, 100,  32,  51];
 static immutable ubyte[4] sigma2 = [ 50,  45,  98, 121];
 static immutable ubyte[4] sigma3 = [116, 101,  32, 107];

 void func(in ubyte[32] key, in ubyte[16] n)
 {
      ubyte[64] buf;
      buf[0..4] = sigma0;
      buf[4..20] = key[0..16];
      buf[20..24] = sigma1;
      buf[24..40] = n;
      buf[40..44] = sigma2;
      buf[44..60] = key[16..$];
      buf[60..64] = sigma3;

     /* do something with buf */
 }
Here's an option that overlays a struct on top of the bytes: struct Buf { union { struct { ubyte[4] sigma0 = [101, 120, 112, 97]; ubyte[16] keyBeg; ubyte[4] sigma1 = [110, 100, 32, 51]; ubyte[16] n; ubyte[4] sigma2 = [ 50, 45, 98, 121]; ubyte[16] keyEnd; ubyte[4] sigma3 = [116, 101, 32, 107]; } ubyte[64] bytes; } this(const ubyte[] key, const ubyte[] n) { this.keyBeg = key[0..16]; this.keyEnd = key[16..$]; this.n = n; } } static assert(Buf.sizeof == 64); void func(in ubyte[] key, in ubyte[] n) { auto buf = Buf(key, n); writeln(buf.bytes); } import std.stdio; import std.range; import std.array; void main() { func(ubyte(32).iota.array, ubyte(16).iota.array); } Prints: [101, 120, 112, 97, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 110, 100, 32, 51, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 50, 45, 98, 121, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, 116, 101, 32, 107] Ali
This looks really nice, but I have several occurences of this, with different arrays (and lengths), so i would need to create several of those structs. But it looks really clean :)
Jul 04 2016
parent reply Rene Zwanenburg <renezwanenburg gmail.com> writes:
On Monday, 4 July 2016 at 19:22:52 UTC, Johannes Loher wrote:
 This looks really nice, but I have several occurences of this, 
 with different arrays (and lengths), so i would need to create 
 several of those structs. But it looks really clean :)
You can use a template to remove the boilerplate. Here's a quick example: https://dpaste.dzfl.pl/43b379992648
Jul 04 2016
parent reply Johannes Loher <johannesloher fg4f.de> writes:
Am 05.07.2016 um 00:41 schrieb Rene Zwanenburg:
 On Monday, 4 July 2016 at 19:22:52 UTC, Johannes Loher wrote:
 This looks really nice, but I have several occurences of this, with
 different arrays (and lengths), so i would need to create several of
 those structs. But it looks really clean :)
You can use a template to remove the boilerplate. Here's a quick example: https://dpaste.dzfl.pl/43b379992648
I tried this, but it does not work correctly with slices.
Jul 05 2016
parent reply Rene Zwanenburg <renezwanenburg gmail.com> writes:
On Tuesday, 5 July 2016 at 12:34:20 UTC, Johannes Loher wrote:
 I tried this, but it does not work correctly with slices.
The length of a slice is a runtime value, which is why it can't be used to set static array size. What were you trying to achieve? Avoid copying the input arrays, or accepting any slice? In case of the first, you can put ref in front of the Args: auto combineArrays(Args...)(ref Args args) The second case will be a bit harder to solve nicely..
Jul 05 2016
parent reply Johannes Loher <johannesloher fg4f.de> writes:
Am 05.07.2016 um 16:39 schrieb Rene Zwanenburg:
 On Tuesday, 5 July 2016 at 12:34:20 UTC, Johannes Loher wrote:
 I tried this, but it does not work correctly with slices.
The length of a slice is a runtime value, which is why it can't be used to set static array size. What were you trying to achieve? Avoid copying the input arrays, or accepting any slice? In case of the first, you can put ref in front of the Args: auto combineArrays(Args...)(ref Args args) The second case will be a bit harder to solve nicely..
I would like to be able, to accept any slice. In the example in my first post, I need this for key[0..16] and key[16..$] (which are slices). Strangely enough, the solution I am leaning towards at the moment also uses length, but it does work for slices of static arrays (i.e. key[0..16]): template expand(alias A) { auto ref M(alias I)() property { return A[I]; } mixin(q{alias expand = TypeTuple!(} ~ iota(A.length).map!(a => "M!" ~ a.to!string).join(",") ~ q{);}); } ubyte[64] buf = [expand!sigma0, expand!key[0..16], expand!sigma1, expand!n, expand!sigma2, expand!key[16..$], expand!sigma3]; I suppose this is because the length of those slices is actually known at copiletime, but it looks a bit strange to me... I was trying to write another template that takes several arrays and expands all of them: template expand(Args...) { mixin(q{alias expand = TypeTuple!(} ~ iota(Args.length).map!(a => "expand!(Args[" ~ a.to!string ~ "])").join(",") ~ q{);}); } It works for static arrays, but not for slices, because the template parameters are not alias parameters, so the length is then not "known" at compile time (at least that's what I think why it fails). Is there a way to specify variadic templates taking any number of alias template parameters?
Jul 05 2016
parent Johannes Loher <johannesloher fg4f.de> writes:
Am 05.07.2016 um 17:12 schrieb Johannes Loher:
 Am 05.07.2016 um 16:39 schrieb Rene Zwanenburg:
 On Tuesday, 5 July 2016 at 12:34:20 UTC, Johannes Loher wrote:
 I tried this, but it does not work correctly with slices.
The length of a slice is a runtime value, which is why it can't be used to set static array size. What were you trying to achieve? Avoid copying the input arrays, or accepting any slice? In case of the first, you can put ref in front of the Args: auto combineArrays(Args...)(ref Args args) The second case will be a bit harder to solve nicely..
I would like to be able, to accept any slice. In the example in my first post, I need this for key[0..16] and key[16..$] (which are slices). Strangely enough, the solution I am leaning towards at the moment also uses length, but it does work for slices of static arrays (i.e. key[0..16]): template expand(alias A) { auto ref M(alias I)() property { return A[I]; } mixin(q{alias expand = TypeTuple!(} ~ iota(A.length).map!(a => "M!" ~ a.to!string).join(",") ~ q{);}); } ubyte[64] buf = [expand!sigma0, expand!key[0..16], expand!sigma1, expand!n, expand!sigma2, expand!key[16..$], expand!sigma3]; I suppose this is because the length of those slices is actually known at copiletime, but it looks a bit strange to me... I was trying to write another template that takes several arrays and expands all of them: template expand(Args...) { mixin(q{alias expand = TypeTuple!(} ~ iota(Args.length).map!(a => "expand!(Args[" ~ a.to!string ~ "])").join(",") ~ q{);}); } It works for static arrays, but not for slices, because the template parameters are not alias parameters, so the length is then not "known" at compile time (at least that's what I think why it fails). Is there a way to specify variadic templates taking any number of alias template parameters?
Well, I just realized my method does not work in the case that I want to use expand on static arrays, which are returned from template functions...
Jul 05 2016
prev sibling parent reply Marc =?UTF-8?B?U2Now7x0eg==?= <schuetzm gmx.net> writes:
auto concat(T : E[n], E, size_t n)(const E[][] args...)  nogc
{
     size_t offset = 0;
     T result = void;
     foreach(arr; args) {
         result[offset .. offset+arr.length] = arr;
         offset += arr.length;
     }
     assert(offset == result.length);
     return result;
}

static immutable ubyte[4] sigma0 = [101, 120, 112,  97];
static immutable ubyte[4] sigma1 = [110, 100,  32,  51];
static immutable ubyte[4] sigma2 = [ 50,  45,  98, 121];
static immutable ubyte[4] sigma3 = [116, 101,  32, 107];

void func(in ref ubyte[32] key, in ref ubyte[16] n)  nogc
{
     ubyte[64] buf;
     buf[0..4] = sigma0;
     buf[4..20] = key[0..16];
     buf[20..24] = sigma1;
     buf[24..40] = n;
     buf[40..44] = sigma2;
     buf[44..60] = key[16..$];
     buf[60..64] = sigma3;

     auto buf2 = concat!(ubyte[64])(sigma0, key[0..16], sigma1, n, 
sigma2, key[16..$], sigma3);

     assert(buf == buf2);
}

void main() {
     ubyte[32] key = 
[0,1,2,3,4,5,6,7,8,9,0,1,2,3,4,5,6,7,8,9,0,1,2,3,4,5,6,7,8,9,0,1];
     ubyte[16] n   = key[0..16];
     func(key, n);
}


Some remarks:

* I added `ref` to `func`'s parameters, because the arrays are 
relatively large, so passing them by value might be costly (you 
should measure it if you care).

* The `void` initialization in `concat` is an optimization that 
is valid only for POD types.

* Returning the array is cheap because of NRVO.
Jul 05 2016
parent Johannes Loher <johannesloher fg4f.de> writes:
Am 05.07.2016 um 17:22 schrieb Marc Schütz:
 auto concat(T : E[n], E, size_t n)(const E[][] args...)  nogc
 {
     size_t offset = 0;
     T result = void;
     foreach(arr; args) {
         result[offset .. offset+arr.length] = arr;
         offset += arr.length;
     }
     assert(offset == result.length);
     return result;
 }
 
 static immutable ubyte[4] sigma0 = [101, 120, 112,  97];
 static immutable ubyte[4] sigma1 = [110, 100,  32,  51];
 static immutable ubyte[4] sigma2 = [ 50,  45,  98, 121];
 static immutable ubyte[4] sigma3 = [116, 101,  32, 107];
 
 void func(in ref ubyte[32] key, in ref ubyte[16] n)  nogc
 {
     ubyte[64] buf;
     buf[0..4] = sigma0;
     buf[4..20] = key[0..16];
     buf[20..24] = sigma1;
     buf[24..40] = n;
     buf[40..44] = sigma2;
     buf[44..60] = key[16..$];
     buf[60..64] = sigma3;
 
     auto buf2 = concat!(ubyte[64])(sigma0, key[0..16], sigma1, n,
 sigma2, key[16..$], sigma3);
 
     assert(buf == buf2);
 }
 
 void main() {
     ubyte[32] key =
 [0,1,2,3,4,5,6,7,8,9,0,1,2,3,4,5,6,7,8,9,0,1,2,3,4,5,6,7,8,9,0,1];
     ubyte[16] n   = key[0..16];
     func(key, n);
 }
 
 
 Some remarks:
 
 * I added `ref` to `func`'s parameters, because the arrays are
 relatively large, so passing them by value might be costly (you should
 measure it if you care).
 
 * The `void` initialization in `concat` is an optimization that is valid
 only for POD types.
 
 * Returning the array is cheap because of NRVO.
This seems to be exactly what I was looking for. Thanks a lot!
Jul 05 2016