Skip to content

way to cast arrays to a smaller array #1376

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Closed
shawnl opened this issue Aug 12, 2018 · 12 comments
Closed

way to cast arrays to a smaller array #1376

shawnl opened this issue Aug 12, 2018 · 12 comments
Labels
proposal This issue suggests modifications. If it also has the "accepted" label then it is planned.
Milestone

Comments

@shawnl
Copy link
Contributor

shawnl commented Aug 12, 2018

I ran into a wall when trying to implement x25519 without being able to slice-and-dice arrays into smaller arrays. (without @ptrCast)

@thejoshwolfe
Copy link
Contributor

are you expecting that to make a copy?

@shawnl
Copy link
Contributor Author

shawnl commented Aug 12, 2018

yes, but generally I wanted to use it as a function call

fn add(a: [32]const u32, b: [32]const u32) [32]u32 {
...
}

var big: [64]u32 = undefined;
_ = add(big[0..32], big[32..64]);

here is x25519 i was working on http://paste.debian.net/1037660/
stuff like this:


    mainloop(@ptrCast(*[64]u32,work[0..].ptr),e);
    @ptrCast(*[32]u32,work[32..].ptr).* = recip(@ptrCast(*[32]u32,work[32..64].ptr).*);

where having a seperate variable just to pass as an argument is silly.

@thejoshwolfe
Copy link
Contributor

that example makes sense. seems like we should be able to make *[n]T with some kind of slice syntax. that way we're comptime-documenting the length of the "slice".

@shawnl
Copy link
Contributor Author

shawnl commented Aug 12, 2018

as seen in the later example: @ptrCast(*[32]u32,work[32..].ptr).* = recip(@ptrCast(*[32]u32,work[32..64].ptr).*); it should also be able to set a subset of a larger array with a smaller array.

@shawnl shawnl changed the title way to cast arrays to a smaller array way to cast arrays to a smaller array and visa versa Aug 12, 2018
@thejoshwolfe
Copy link
Contributor

wait. is this issue about copying or referencing?

@shawnl
Copy link
Contributor Author

shawnl commented Aug 12, 2018

referencing, but as the function does not modify the array, copying does the same thing in this case.

@thejoshwolfe
Copy link
Contributor

ok. the OP was misleading then.

// this designates a storage location for 2 u32's
var smaller_array: [2]u32 = ...;

// this parameter does not necessitate any actual storage,
// and will in practice be a const reference, not a copy.
fn foo(a: [2]u32) void {}

// this is definitely a reference, not a copy.
var a: *[2]u32 = ...;

i think the smaller_array example from the OP should not work. perhaps there is something that should work with the a examples in this comment.

here's an idea:

var array: [64]u32 = ...;
var first_half: *[32]u32 = array[0..32];
var second_half: *[32]u32 = array[32..];

In order for this to work, the compiler would need to know the slicing index/bounds at comptime. That's kind of an awkward requirement, since the comptime-knownness of values determines what kinds of type casts are allowed.

More specifically, the compiler really only needs to know the length of the resulting slice, not the start index. That's even more awkward, since you could do array[x..x+32], where x is not comptime known; then neither of the slice values are comptime known, but the length of the slice could be known if you pay sophisticated enough attention to the slicing expressions.

This is leading me to think that maybe this operation should really be a different feature. How about this builtin function:

var array: [64]u32 = ...;
var first_half: *[32]u32 = @arrayPtr(array, 0, 32);
var first_half: *[32]u32 = @arrayPtr(array, 32, 32);

// pseudocode signature:
fn @arrayPtr(array: [n]T, start: usize, comptime len: usize) *[len]T;

I can almost imagine this being implemented in userspace, except for some complications with propagating const qualifiers, and maybe the function should also take a slice in addition to an array.

@Hejsil
Copy link
Contributor

Hejsil commented Aug 12, 2018

I think #863 is related

@thejoshwolfe
Copy link
Contributor

@Hejsil ah, yes. that's what i was going for.

@shawnl i'm still confused why "visa versa" is in the title of this issue. wouldn't "casting" from a smaller array into a larger array only be meaningful with copying?

@andrewrk andrewrk added this to the 0.4.0 milestone Aug 13, 2018
@andrewrk andrewrk added the proposal This issue suggests modifications. If it also has the "accepted" label then it is planned. label Aug 13, 2018
@shawnl shawnl changed the title way to cast arrays to a smaller array and visa versa way to cast arrays to a smaller array Aug 14, 2018
@tgschultz
Copy link
Contributor

tgschultz commented Aug 15, 2018

I went ahead and implemented a variation of @arrayPtr in userspace for the next version of my utils package. It was, as predicted, just a little hairy because of the const/non-const distinction, but I'd already solved that for a different function in utils. I've copied appropriate support functions into this code for convenience.

PS: Forgive the non-standard formatting, as I'm an Allman native.

fn isConst(comptime T: type) bool
{
    comptime
    {
        if(!isPointer(T)) return false;
        const info = @typeInfo(T);
        return info.Pointer.is_const;
    }
}

fn isPointer(comptime T: type) bool
{
    comptime
    {
        const inp_type_id = @typeId(T);
        return inp_type_id == builtin.TypeId.Pointer;
    }
}

fn isSingleItemPointer(comptime T: type) bool
{
    comptime
    {
        if(isPointer(T))
        {
            const info = @typeInfo(T);
            return info.Pointer.size == builtin.TypeInfo.Pointer.Size.One;
        }
        return false;
    }
}

fn isArray(comptime T: type) bool
{
    comptime
    {
        const inp_type_id = @typeId(T);
        return inp_type_id == builtin.TypeId.Array;
    }
}

fn _ArraySliceReturnType(comptime T: type, comptime len: usize) type
{
    debug.assert(isSingleItemPointer(T));
    debug.assert(isArray(T.Child));
    
    if(isConst(T)) return *const [len]T.Child.Child;
    return *[len]T.Child.Child;
}

pub fn arraySlice(ptr: var, comptime start: usize, comptime len: usize)
    _ArraySliceReturnType(@typeOf(ptr), len)
{
    debug.assert(isSingleItemPointer(@typeOf(ptr)));
    debug.assert(isArray(@typeOf(ptr).Child));
    
    return @ptrCast(_ArraySliceReturnType(@typeOf(ptr), len), &ptr.*[start]);
}

test "arraySlice"
{
    const array = comptime arr:
    {
        var temp = []u32{0} ** 64;
        for(temp) |*e, i| e.* = @intCast(u32, i);
        break :arr temp;
    };
    
    const first_half = arraySlice(&array, 0, 32);
    const second_half = arraySlice(&array, 32, 32);
    
    for(first_half.*) |e, i|  debug.assert(i == e);
    
    for(second_half.*) |e, i| debug.assert(i + 32 == e);
}

@andrewrk andrewrk modified the milestones: 0.4.0, 0.5.0 Sep 28, 2018
@tgschultz
Copy link
Contributor

It should be noted that this has long since been included in std.meta.

@andrewrk
Copy link
Member

andrewrk commented Jul 2, 2019

I'm fairly certain this is a duplicate of the accepted proposal #863

@andrewrk andrewrk closed this as completed Jul 2, 2019
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
proposal This issue suggests modifications. If it also has the "accepted" label then it is planned.
Projects
None yet
Development

No branches or pull requests

5 participants