SE-184 Improved Pointers

I’m fine with switching to taking the count from the source, though I think
taking the count from the destination is slightly better because 1) the use
cases I mentioned in the other email, and 2) all the other memorystate
functions use self.count instead of source.count, if they take a source
argument. But being consistent with the raw pointer version is more
important.

Should the methods that don’t deal with raw buffers also be modified to use
the source argument (i.e. UnsafeMutableBufferPointer.initialize(from:))?

Also, was there a reason why UnsafeMutableRawBufferPointer.copyBytes(from:)
uses the source’s count instead of its own? Right now this behavior is
“technically” undocumented behavior (as the public docs haven’t been
updated) so if there was ever a time to change it, now would be it.

···

On Wed, Aug 9, 2017 at 1:51 AM, Andrew Trick <atrick@apple.com> wrote:

On Aug 8, 2017, at 8:44 PM, Taylor Swift <kelvin13ma@gmail.com> wrote:

cool,, as for UnsafeMutableRawBufferPointer.copy(from:bytes:), I cannot
find such a function anywhere in the API. There is copyBytes(from:)
<https://developer.apple.com/documentation/swift/unsafemutablerawbufferpointer/2635415-copybytes&gt;,
but the documentation is messed up and mentions a nonexistent count:
argument over and over again. The documentation also doesn’t mention what
happens if there is a length mismatch, so users are effectively relying on
an implementation detail. I don’t know how to best resolve this.

We currently have `UnsafeMutableRawBufferPointer.copyBytes(from:)`. I
don’t think your proposal changes that. The current docs refer to the
`source` parameter, which is correct. Docs refer to the parameter name, not
the label name. So `source.count` is the size of the input. I was pointing
out that it has the semantics: `debugAssert(source.count <= self.count)`.

Your proposal changes `UnsafeRawPointer.copyBytes(from:count:)` to
`UnsafeRawPointer.copy(from:bytes:)`. Originally we wanted to those API
names to match, but I’m fine with your change. What is more important is
that the semantics are the same as `copyBytes(from:)`. Furthermore, any new
methods that you add that copy into a raw buffer (e.g.
initializeMemory(as:from:count:)) should have similar behavior.

Another thing. The initialization methods that you’re adding to
`UnsafeRawPointer` and `UnsafeRawBufferPointer` should return typed
`UnsafePointer<Element>` and `UnsafeBufferPointer<Element>` respectively.

I’ll fix that once the current pending edit
<https://github.com/apple/swift-evolution/pull/741&gt; gets merged.

Thanks,

-Andy

On Tue, Aug 8, 2017 at 11:33 PM, Andrew Trick <atrick@apple.com> wrote:

On Aug 8, 2017, at 8:29 PM, Taylor Swift <kelvin13ma@gmail.com> wrote:

On Tue, Aug 8, 2017 at 11:24 PM, Andrew Trick <atrick@apple.com> wrote:

On Aug 8, 2017, at 6:51 PM, Taylor Swift <kelvin13ma@gmail.com> wrote:

On Tue, Aug 8, 2017 at 9:38 PM, Andrew Trick <atrick@apple.com> wrote:

> UnsafeMutableRawBufferPointer.allocate(bytes:alignedTo:)

Well, I think it's somewhat ridiculous for users to write this every
time they allocate a buffer:

`UnsafeMutableRawBufferPointer.allocate(bytes: size, alignedTo:
MemoryLayout<UInt>.alignment)`

If anyone reading the code is unsure about the Swift API's alignment
guarantee, it's trivial to check the API docs.

You could introduce a clearly documented default `alignedTo`
argument. The reason I didn't do that is that the runtime won't
respect it anyway. But I think it would be fair to go ahead with the
API and file a bug against the runtime.

Default argument of MemoryLayout<Int>.alignment is the way to go but
as you said i don’t know if that is actually allowed/works. An alternative
is to have two allocate methods each, one that takes an alignment argument
and one that doesn’t (and aligns to pointer alignment) but that feels
inelegant. Default arguments would be better.

Default argument makes sense to me too. Then the raw buffer pointer and
regular raw pointer APIs can be consistent with each other.

Runtime bug: [SR-5664] UnsafeRawPointer.allocate(bytes:alignedTo:) does not respect alignment. · Issue #48234 · apple/swift · GitHub

yikes i was not aware of this. I don’t think it’s bad enough to warrant
dropping the argument like with deallocate(capacity:) but I can imagine
bad things happening to code that crams extra inhabitants into pointers.

If we ever need to do pointer adjustment during deallocation to
accommodate alignment, then I think the Swift runtime can track that. I see
no reason to muddy the UnsafeRawPointer API with it. So, I agree with your
proposed change to drop `alignedTo` there.

-Andy

oh lol I was talking about assuming the pointer returned by
allocate(bytes:alignedTo:) is a multiple of alignedTo. Some code might
be relying on the last few bits of the pointer being zero; i.e. sticking
bit flags there like how some implementations store the red/black color
information in a red-black tree node.

Oh, sure. But I think it will be easy to fix the runtime. We could
probably do it before the proposal is accepted if necessary.
-Andy

Added
<https://github.com/kelvin13/swift/commit/b457366150715ab86925e767872568cc8ea6aaa1&gt;
those methods to the implementation and updated the proposal document.

···

On Fri, Aug 18, 2017 at 11:42 PM, Andrew Trick <atrick@apple.com> wrote:

On Aug 18, 2017, at 5:36 PM, Taylor Swift <kelvin13ma@gmail.com> wrote:

Should the immutable buffer pointer types also get deallocate()?

Both UnsafePointer and UnsafeBufferPointer should get deallocate. The Raw
API already has those methods.

-Andy

On Fri, Aug 18, 2017 at 7:55 PM, Andrew Trick <atrick@apple.com> wrote:

On Aug 15, 2017, at 9:47 PM, Taylor Swift via swift-evolution < >> swift-evolution@swift.org> wrote:

Implementation is here: [do not merge] [stdlib] Improved pointers by tayloraswift · Pull Request #11464 · apple/swift · GitHub

On Sat, Aug 12, 2017 at 8:23 PM, Taylor Swift <kelvin13ma@gmail.com> >> wrote:

I’ve revised the proposal based on what I learned from trying to
implement these changes. I think it’s worth tacking the existing methods
that take Sequences at the same time as this actually makes the design
a bit simpler.
<https://gist.github.com/kelvin13/5edaf43dcd3d6d9ed24f303fc941214c&gt;

*The previous version
<https://gist.github.com/kelvin13/1b8ae906be23dff22f7a7c4767f0c907&gt; of this
document ignored the generic initialization methods on
UnsafeMutableBufferPointer and UnsafeMutableRawBufferPointer, leaving them
to be overhauled at a later date, in a separate proposal. Instead, this
version of the proposal leverages those existing methods to inform a more
compact API design which has less surface area, and is more future-proof
since it obviates the need to design and add another (redundant) set of
protocol-oriented pointer APIs later.*

On Tue, Aug 8, 2017 at 12:52 PM, Taylor Swift <kelvin13ma@gmail.com> >>> wrote:

Since Swift 5 just got opened up for proposals, SE-184 Improved
Pointers is ready for community review, and I encourage everyone to look it
over and provide feedback. Thank you!
<https://github.com/apple/swift-evolution/blob/master/propos
als/0184-improved-pointers.md>

Would you mind adding a deallocate method to (nonmutable)
UnsafePointer/UnsafeBufferPointer to take care of
[SR-3309](https://bugs.swift.org/browse/SR-3309\)?

There’s simply nothing in the memory model that requires mutable memory
for deallocation.

It fits right in with this proposal and hardly seems worth a separate one.

-Andy

cool,, as for UnsafeMutableRawBufferPointer.copy(from:bytes:), I cannot find such a function anywhere in the API. There is copyBytes(from:) <https://developer.apple.com/documentation/swift/unsafemutablerawbufferpointer/2635415-copybytes&gt;, but the documentation is messed up and mentions a nonexistent count: argument over and over again. The documentation also doesn’t mention what happens if there is a length mismatch, so users are effectively relying on an implementation detail. I don’t know how to best resolve this.

We currently have `UnsafeMutableRawBufferPointer.copyBytes(from:)`. I don’t think your proposal changes that. The current docs refer to the `source` parameter, which is correct. Docs refer to the parameter name, not the label name. So `source.count` is the size of the input. I was pointing out that it has the semantics: `debugAssert(source.count <= self.count)`.

Your proposal changes `UnsafeRawPointer.copyBytes(from:count:)` to `UnsafeRawPointer.copy(from:bytes:)`. Originally we wanted to those API names to match, but I’m fine with your change. What is more important is that the semantics are the same as `copyBytes(from:)`. Furthermore, any new methods that you add that copy into a raw buffer (e.g. initializeMemory(as:from:count:)) should have similar behavior.

I’m fine with switching to taking the count from the source, though I think taking the count from the destination is slightly better because 1) the use cases I mentioned in the other email, and 2) all the other memorystate functions use self.count instead of source.count, if they take a source argument. But being consistent with the raw pointer version is more important.

If it’s copying from a buffer it should not take a count, if it’s copying from a pointer it obviously needs to take a count. What I mean by the two versions being named consistently is simply that they’re both named `copyBytes`. That really isn’t important though. The overflow/underflow semantics being consistent are important.

(Incidentally, the reason “bytes” needs to be in the somewhere name is because this method isn’t capable of copying nontrivial values)

Should the methods that don’t deal with raw buffers also be modified to use the source argument (i.e. UnsafeMutableBufferPointer.initialize(from:))?

I’m not sure what you mean by this. It also allows the destination to be larger than the source. Initializing from a sequence does not trap on overflow because we can’t guarantee the size of the sequence ahead of time. When I talk about consistent overflow/underflow semantics, I’m only talking about initializing one unsafe buffer/pointer from another unsafe buffer/pointer.

Also, was there a reason why UnsafeMutableRawBufferPointer.copyBytes(from:) uses the source’s count instead of its own? Right now this behavior is “technically” undocumented behavior (as the public docs haven’t been updated) so if there was ever a time to change it, now would be it.

Mainly because partial initialization is more expected than dropping data on the floor. Ultimately, this should be whatever typical developers would expect the behavior to be. I would be very hesitant to change the behavior now though.

-Andy

···

On Aug 8, 2017, at 11:10 PM, Taylor Swift <kelvin13ma@gmail.com> wrote:
On Wed, Aug 9, 2017 at 1:51 AM, Andrew Trick <atrick@apple.com <mailto:atrick@apple.com>> wrote:

On Aug 8, 2017, at 8:44 PM, Taylor Swift <kelvin13ma@gmail.com <mailto:kelvin13ma@gmail.com>> wrote:

Another thing. The initialization methods that you’re adding to `UnsafeRawPointer` and `UnsafeRawBufferPointer` should return typed `UnsafePointer<Element>` and `UnsafeBufferPointer<Element>` respectively.

I’ll fix that once the current pending edit <https://github.com/apple/swift-evolution/pull/741&gt; gets merged.

Thanks,

-Andy

On Tue, Aug 8, 2017 at 11:33 PM, Andrew Trick <atrick@apple.com <mailto:atrick@apple.com>> wrote:

On Aug 8, 2017, at 8:29 PM, Taylor Swift <kelvin13ma@gmail.com <mailto:kelvin13ma@gmail.com>> wrote:

On Tue, Aug 8, 2017 at 11:24 PM, Andrew Trick <atrick@apple.com <mailto:atrick@apple.com>> wrote:

On Aug 8, 2017, at 6:51 PM, Taylor Swift <kelvin13ma@gmail.com <mailto:kelvin13ma@gmail.com>> wrote:

On Tue, Aug 8, 2017 at 9:38 PM, Andrew Trick <atrick@apple.com <mailto:atrick@apple.com>> wrote:

> UnsafeMutableRawBufferPointer.allocate(bytes:alignedTo:)

Well, I think it's somewhat ridiculous for users to write this every time they allocate a buffer:

`UnsafeMutableRawBufferPointer.allocate(bytes: size, alignedTo: MemoryLayout<UInt>.alignment)`

If anyone reading the code is unsure about the Swift API's alignment
guarantee, it's trivial to check the API docs.

You could introduce a clearly documented default `alignedTo`
argument. The reason I didn't do that is that the runtime won't
respect it anyway. But I think it would be fair to go ahead with the
API and file a bug against the runtime.

Default argument of MemoryLayout<Int>.alignment is the way to go but as you said i don’t know if that is actually allowed/works. An alternative is to have two allocate methods each, one that takes an alignment argument and one that doesn’t (and aligns to pointer alignment) but that feels inelegant. Default arguments would be better.

Default argument makes sense to me too. Then the raw buffer pointer and regular raw pointer APIs can be consistent with each other.

Runtime bug: [SR-5664] UnsafeRawPointer.allocate(bytes:alignedTo:) does not respect alignment. · Issue #48234 · apple/swift · GitHub

yikes i was not aware of this. I don’t think it’s bad enough to warrant dropping the argument like with deallocate(capacity:) but I can imagine bad things happening to code that crams extra inhabitants into pointers.

If we ever need to do pointer adjustment during deallocation to accommodate alignment, then I think the Swift runtime can track that. I see no reason to muddy the UnsafeRawPointer API with it. So, I agree with your proposed change to drop `alignedTo` there.

-Andy

oh lol I was talking about assuming the pointer returned by allocate(bytes:alignedTo:) is a multiple of alignedTo. Some code might be relying on the last few bits of the pointer being zero; i.e. sticking bit flags there like how some implementations store the red/black color information in a red-black tree node.

Oh, sure. But I think it will be easy to fix the runtime. We could probably do it before the proposal is accepted if necessary.
-Andy

cool,, as for UnsafeMutableRawBufferPointer.copy(from:bytes:), I cannot
find such a function anywhere in the API. There is copyBytes(from:)
<https://developer.apple.com/documentation/swift/unsafemutablerawbufferpointer/2635415-copybytes&gt;,
but the documentation is messed up and mentions a nonexistent count:
argument over and over again. The documentation also doesn’t mention what
happens if there is a length mismatch, so users are effectively relying on
an implementation detail. I don’t know how to best resolve this.

We currently have `UnsafeMutableRawBufferPointer.copyBytes(from:)`. I
don’t think your proposal changes that. The current docs refer to the
`source` parameter, which is correct. Docs refer to the parameter name, not
the label name. So `source.count` is the size of the input. I was pointing
out that it has the semantics: `debugAssert(source.count <= self.count)`.

Your proposal changes `UnsafeRawPointer.copyBytes(from:count:)` to
`UnsafeRawPointer.copy(from:bytes:)`. Originally we wanted to those API
names to match, but I’m fine with your change. What is more important is
that the semantics are the same as `copyBytes(from:)`. Furthermore, any new
methods that you add that copy into a raw buffer (e.g.
initializeMemory(as:from:count:)) should have similar behavior.

I’m fine with switching to taking the count from the source, though I
think taking the count from the destination is slightly better because 1)
the use cases I mentioned in the other email, and 2) all the other
memorystate functions use self.count instead of source.count, if they
take a source argument. But being consistent with the raw pointer version
is more important.

If it’s copying from a buffer it should not take a count, if it’s copying
from a pointer it obviously needs to take a count. What I mean by the two
versions being named consistently is simply that they’re both named
`copyBytes`. That really isn’t important though. The overflow/underflow
semantics being consistent are important.

(Incidentally, the reason “bytes” needs to be in the somewhere name is
because this method isn’t capable of copying nontrivial values)

Should the methods that don’t deal with raw buffers also be modified to
use the source argument (i.e. UnsafeMutableBufferPointer.
initialize(from:))?

I’m not sure what you mean by this. It also allows the destination to be
larger than the source. Initializing from a sequence does not trap on
overflow because we can’t guarantee the size of the sequence ahead of time.
When I talk about consistent overflow/underflow semantics, I’m only talking
about initializing one unsafe buffer/pointer from another unsafe
buffer/pointer.

Also, was there a reason why UnsafeMutableRawBufferPointer.
copyBytes(from:) uses the source’s count instead of its own? Right now
this behavior is “technically” undocumented behavior (as the public docs
haven’t been updated) so if there was ever a time to change it, now would
be it.

Mainly because partial initialization is more expected than dropping data
on the floor. Ultimately, this should be whatever typical developers would
expect the behavior to be. I would be very hesitant to change the behavior
now though.

-Andy

The problem is I would expect to be able to safely call deinitialize() and
friends after calling initialize(from:). If Element is a class type and
initialize doesn’t fill the entire buffer range, calling deinitialize()
will crash. That being said, since copy(from:bytes:) and copyBytes(from:)
don’t do any initialization and have no direct counterparts in
UnsafeMutableBufferPointer, it’s okay if they have different behavior than
the other methods.

···

On Wed, Aug 9, 2017 at 2:34 AM, Andrew Trick <atrick@apple.com> wrote:

On Aug 8, 2017, at 11:10 PM, Taylor Swift <kelvin13ma@gmail.com> wrote:
On Wed, Aug 9, 2017 at 1:51 AM, Andrew Trick <atrick@apple.com> wrote:

On Aug 8, 2017, at 8:44 PM, Taylor Swift <kelvin13ma@gmail.com> wrote:

Another thing. The initialization methods that you’re adding to
`UnsafeRawPointer` and `UnsafeRawBufferPointer` should return typed
`UnsafePointer<Element>` and `UnsafeBufferPointer<Element>` respectively.

I’ll fix that once the current pending edit
<https://github.com/apple/swift-evolution/pull/741&gt; gets merged.

Thanks,

-Andy

On Tue, Aug 8, 2017 at 11:33 PM, Andrew Trick <atrick@apple.com> wrote:

On Aug 8, 2017, at 8:29 PM, Taylor Swift <kelvin13ma@gmail.com> wrote:

On Tue, Aug 8, 2017 at 11:24 PM, Andrew Trick <atrick@apple.com> wrote:

On Aug 8, 2017, at 6:51 PM, Taylor Swift <kelvin13ma@gmail.com> wrote:

On Tue, Aug 8, 2017 at 9:38 PM, Andrew Trick <atrick@apple.com> wrote:

> UnsafeMutableRawBufferPointer.allocate(bytes:alignedTo:)

Well, I think it's somewhat ridiculous for users to write this every
time they allocate a buffer:

`UnsafeMutableRawBufferPointer.allocate(bytes: size, alignedTo:
MemoryLayout<UInt>.alignment)`

If anyone reading the code is unsure about the Swift API's alignment
guarantee, it's trivial to check the API docs.

You could introduce a clearly documented default `alignedTo`
argument. The reason I didn't do that is that the runtime won't
respect it anyway. But I think it would be fair to go ahead with the
API and file a bug against the runtime.

Default argument of MemoryLayout<Int>.alignment is the way to go but
as you said i don’t know if that is actually allowed/works. An alternative
is to have two allocate methods each, one that takes an alignment argument
and one that doesn’t (and aligns to pointer alignment) but that feels
inelegant. Default arguments would be better.

Default argument makes sense to me too. Then the raw buffer pointer
and regular raw pointer APIs can be consistent with each other.

Runtime bug: [SR-5664] UnsafeRawPointer.allocate(bytes:alignedTo:) does not respect alignment. · Issue #48234 · apple/swift · GitHub

yikes i was not aware of this. I don’t think it’s bad enough to warrant
dropping the argument like with deallocate(capacity:) but I can
imagine bad things happening to code that crams extra inhabitants into
pointers.

If we ever need to do pointer adjustment during deallocation to
accommodate alignment, then I think the Swift runtime can track that. I see
no reason to muddy the UnsafeRawPointer API with it. So, I agree with your
proposed change to drop `alignedTo` there.

-Andy

oh lol I was talking about assuming the pointer returned by
allocate(bytes:alignedTo:) is a multiple of alignedTo. Some code might
be relying on the last few bits of the pointer being zero; i.e. sticking
bit flags there like how some implementations store the red/black color
information in a red-black tree node.

Oh, sure. But I think it will be easy to fix the runtime. We could
probably do it before the proposal is accepted if necessary.
-Andy

okay so I’m implementing <https://github.com/kelvin13/swift&gt; this rn, and i
just realized that the functions
UnsafeMutableRawBufferPointer.initializeMemory<T>(as:from:count:) and
UnsafeMutableRawBufferPointer.moveInitializeMemory<T>(as:from:count:) are
gonna be a problem. Since they take a strided buffer pointer as its
argument, should they lose the count: argument and take the count value
from source.count? However, this would go opposite of the direction in most
of the typed buffer pointer methods, where count comes from self.count, not
source.count.

···

On Wed, Aug 9, 2017 at 11:51 AM, Taylor Swift via swift-evolution < swift-evolution@swift.org> wrote:

On Wed, Aug 9, 2017 at 2:34 AM, Andrew Trick <atrick@apple.com> wrote:

On Aug 8, 2017, at 11:10 PM, Taylor Swift <kelvin13ma@gmail.com> wrote:

On Wed, Aug 9, 2017 at 1:51 AM, Andrew Trick <atrick@apple.com> wrote:

On Aug 8, 2017, at 8:44 PM, Taylor Swift <kelvin13ma@gmail.com> wrote:

cool,, as for UnsafeMutableRawBufferPointer.copy(from:bytes:), I cannot
find such a function anywhere in the API. There is copyBytes(from:)
<https://developer.apple.com/documentation/swift/unsafemutablerawbufferpointer/2635415-copybytes&gt;,
but the documentation is messed up and mentions a nonexistent count:
argument over and over again. The documentation also doesn’t mention what
happens if there is a length mismatch, so users are effectively relying on
an implementation detail. I don’t know how to best resolve this.

We currently have `UnsafeMutableRawBufferPointer.copyBytes(from:)`. I
don’t think your proposal changes that. The current docs refer to the
`source` parameter, which is correct. Docs refer to the parameter name, not
the label name. So `source.count` is the size of the input. I was pointing
out that it has the semantics: `debugAssert(source.count <= self.count)`.

Your proposal changes `UnsafeRawPointer.copyBytes(from:count:)` to
`UnsafeRawPointer.copy(from:bytes:)`. Originally we wanted to those API
names to match, but I’m fine with your change. What is more important is
that the semantics are the same as `copyBytes(from:)`. Furthermore, any new
methods that you add that copy into a raw buffer (e.g.
initializeMemory(as:from:count:)) should have similar behavior.

I’m fine with switching to taking the count from the source, though I
think taking the count from the destination is slightly better because
1) the use cases I mentioned in the other email, and 2) all the other
memorystate functions use self.count instead of source.count, if they
take a source argument. But being consistent with the raw pointer
version is more important.

If it’s copying from a buffer it should not take a count, if it’s copying
from a pointer it obviously needs to take a count. What I mean by the two
versions being named consistently is simply that they’re both named
`copyBytes`. That really isn’t important though. The overflow/underflow
semantics being consistent are important.

(Incidentally, the reason “bytes” needs to be in the somewhere name is
because this method isn’t capable of copying nontrivial values)

Should the methods that don’t deal with raw buffers also be modified to
use the source argument (i.e. UnsafeMutableBufferPointer.ini
tialize(from:))?

I’m not sure what you mean by this. It also allows the destination to be
larger than the source. Initializing from a sequence does not trap on
overflow because we can’t guarantee the size of the sequence ahead of time.
When I talk about consistent overflow/underflow semantics, I’m only talking
about initializing one unsafe buffer/pointer from another unsafe
buffer/pointer.

Also, was there a reason why UnsafeMutableRawBufferPointer.
copyBytes(from:) uses the source’s count instead of its own? Right now
this behavior is “technically” undocumented behavior (as the public docs
haven’t been updated) so if there was ever a time to change it, now would
be it.

Mainly because partial initialization is more expected than dropping data
on the floor. Ultimately, this should be whatever typical developers would
expect the behavior to be. I would be very hesitant to change the behavior
now though.

-Andy

The problem is I would expect to be able to safely call deinitialize() and
friends after calling initialize(from:). If Element is a class type and
initialize doesn’t fill the entire buffer range, calling deinitialize()
will crash. That being said, since copy(from:bytes:) and copyBytes(from:)
don’t do any initialization and have no direct counterparts in
UnsafeMutableBufferPointer, it’s okay if they have different behavior than
the other methods.

Another thing. The initialization methods that you’re adding to
`UnsafeRawPointer` and `UnsafeRawBufferPointer` should return typed
`UnsafePointer<Element>` and `UnsafeBufferPointer<Element>` respectively.

I’ll fix that once the current pending edit
<https://github.com/apple/swift-evolution/pull/741&gt; gets merged.

Thanks,

-Andy

On Tue, Aug 8, 2017 at 11:33 PM, Andrew Trick <atrick@apple.com> wrote:

On Aug 8, 2017, at 8:29 PM, Taylor Swift <kelvin13ma@gmail.com> wrote:

On Tue, Aug 8, 2017 at 11:24 PM, Andrew Trick <atrick@apple.com> wrote:

On Aug 8, 2017, at 6:51 PM, Taylor Swift <kelvin13ma@gmail.com> wrote:

On Tue, Aug 8, 2017 at 9:38 PM, Andrew Trick <atrick@apple.com> wrote:

> UnsafeMutableRawBufferPointer.allocate(bytes:alignedTo:)

Well, I think it's somewhat ridiculous for users to write this every
time they allocate a buffer:

`UnsafeMutableRawBufferPointer.allocate(bytes: size, alignedTo:
MemoryLayout<UInt>.alignment)`

If anyone reading the code is unsure about the Swift API's alignment
guarantee, it's trivial to check the API docs.

You could introduce a clearly documented default `alignedTo`
argument. The reason I didn't do that is that the runtime won't
respect it anyway. But I think it would be fair to go ahead with the
API and file a bug against the runtime.

Default argument of MemoryLayout<Int>.alignment is the way to go but
as you said i don’t know if that is actually allowed/works. An alternative
is to have two allocate methods each, one that takes an alignment argument
and one that doesn’t (and aligns to pointer alignment) but that feels
inelegant. Default arguments would be better.

Default argument makes sense to me too. Then the raw buffer pointer
and regular raw pointer APIs can be consistent with each other.

Runtime bug: [SR-5664] UnsafeRawPointer.allocate(bytes:alignedTo:) does not respect alignment. · Issue #48234 · apple/swift · GitHub

yikes i was not aware of this. I don’t think it’s bad enough to
warrant dropping the argument like with deallocate(capacity:) but I
can imagine bad things happening to code that crams extra inhabitants into
pointers.

If we ever need to do pointer adjustment during deallocation to
accommodate alignment, then I think the Swift runtime can track that. I see
no reason to muddy the UnsafeRawPointer API with it. So, I agree with your
proposed change to drop `alignedTo` there.

-Andy

oh lol I was talking about assuming the pointer returned by
allocate(bytes:alignedTo:) is a multiple of alignedTo. Some code might
be relying on the last few bits of the pointer being zero; i.e. sticking
bit flags there like how some implementations store the red/black color
information in a red-black tree node.

Oh, sure. But I think it will be easy to fix the runtime. We could
probably do it before the proposal is accepted if necessary.
-Andy

_______________________________________________
swift-evolution mailing list
swift-evolution@swift.org
https://lists.swift.org/mailman/listinfo/swift-evolution

You astutely pointed out that the UnsafeMutableBufferPointer.deinitialize() method is dangerous, and I asked you to add a warning to its comments. However, given the danger, I think we need to justify adding the method to begin with. Are there real use cases that greatly benefit from it?

We have to accept that UnsafeBufferPointer is simply not a safe API for managing initialization and deinitialization. Adding a convenience method only makes it less safe.

The standard library *should* vend a safe API for initializing and deinitializing manually allocated memory. However, that will require a new "buffer" type that wraps UnsafeBufferPointer. That will be a major design discussion that is both out of scope for Swift 5 and makes sense to defer until we have move-only types.

-Andy

···

On Aug 9, 2017, at 8:51 AM, Taylor Swift <kelvin13ma@gmail.com> wrote:

On Wed, Aug 9, 2017 at 2:34 AM, Andrew Trick <atrick@apple.com <mailto:atrick@apple.com>> wrote:

On Aug 8, 2017, at 11:10 PM, Taylor Swift <kelvin13ma@gmail.com <mailto:kelvin13ma@gmail.com>> wrote:

On Wed, Aug 9, 2017 at 1:51 AM, Andrew Trick <atrick@apple.com <mailto:atrick@apple.com>> wrote:

On Aug 8, 2017, at 8:44 PM, Taylor Swift <kelvin13ma@gmail.com <mailto:kelvin13ma@gmail.com>> wrote:

cool,, as for UnsafeMutableRawBufferPointer.copy(from:bytes:), I cannot find such a function anywhere in the API. There is copyBytes(from:) <https://developer.apple.com/documentation/swift/unsafemutablerawbufferpointer/2635415-copybytes&gt;, but the documentation is messed up and mentions a nonexistent count: argument over and over again. The documentation also doesn’t mention what happens if there is a length mismatch, so users are effectively relying on an implementation detail. I don’t know how to best resolve this.

We currently have `UnsafeMutableRawBufferPointer.copyBytes(from:)`. I don’t think your proposal changes that. The current docs refer to the `source` parameter, which is correct. Docs refer to the parameter name, not the label name. So `source.count` is the size of the input. I was pointing out that it has the semantics: `debugAssert(source.count <= self.count)`.

Your proposal changes `UnsafeRawPointer.copyBytes(from:count:)` to `UnsafeRawPointer.copy(from:bytes:)`. Originally we wanted to those API names to match, but I’m fine with your change. What is more important is that the semantics are the same as `copyBytes(from:)`. Furthermore, any new methods that you add that copy into a raw buffer (e.g. initializeMemory(as:from:count:)) should have similar behavior.

I’m fine with switching to taking the count from the source, though I think taking the count from the destination is slightly better because 1) the use cases I mentioned in the other email, and 2) all the other memorystate functions use self.count instead of source.count, if they take a source argument. But being consistent with the raw pointer version is more important.

If it’s copying from a buffer it should not take a count, if it’s copying from a pointer it obviously needs to take a count. What I mean by the two versions being named consistently is simply that they’re both named `copyBytes`. That really isn’t important though. The overflow/underflow semantics being consistent are important.

(Incidentally, the reason “bytes” needs to be in the somewhere name is because this method isn’t capable of copying nontrivial values)

Should the methods that don’t deal with raw buffers also be modified to use the source argument (i.e. UnsafeMutableBufferPointer.initialize(from:))?

I’m not sure what you mean by this. It also allows the destination to be larger than the source. Initializing from a sequence does not trap on overflow because we can’t guarantee the size of the sequence ahead of time. When I talk about consistent overflow/underflow semantics, I’m only talking about initializing one unsafe buffer/pointer from another unsafe buffer/pointer.

Also, was there a reason why UnsafeMutableRawBufferPointer.copyBytes(from:) uses the source’s count instead of its own? Right now this behavior is “technically” undocumented behavior (as the public docs haven’t been updated) so if there was ever a time to change it, now would be it.

Mainly because partial initialization is more expected than dropping data on the floor. Ultimately, this should be whatever typical developers would expect the behavior to be. I would be very hesitant to change the behavior now though.

-Andy

The problem is I would expect to be able to safely call deinitialize() and friends after calling initialize(from:). If Element is a class type and initialize doesn’t fill the entire buffer range, calling deinitialize() will crash. That being said, since copy(from:bytes:) and copyBytes(from:) don’t do any initialization and have no direct counterparts in UnsafeMutableBufferPointer, it’s okay if they have different behavior than the other methods.

I agree that’s a problem, which is why i was iffy on supporting partial
initialization to begin with. The use case is for things like growing
collections where you have to periodically move to larger storage. However,
deinitialize is no more dangerous than moveInitialize,
assign(repeating:count:), or moveAssign; they all deinitialize at least one
entire buffer. If deinitialize is to be omitted, so must a majority of the
unsafe pointer API.

···

On Sat, Aug 19, 2017 at 6:05 PM, Andrew Trick <atrick@apple.com> wrote:

On Aug 9, 2017, at 8:51 AM, Taylor Swift <kelvin13ma@gmail.com> wrote:

On Wed, Aug 9, 2017 at 2:34 AM, Andrew Trick <atrick@apple.com> wrote:

On Aug 8, 2017, at 11:10 PM, Taylor Swift <kelvin13ma@gmail.com> wrote:

On Wed, Aug 9, 2017 at 1:51 AM, Andrew Trick <atrick@apple.com> wrote:

On Aug 8, 2017, at 8:44 PM, Taylor Swift <kelvin13ma@gmail.com> wrote:

cool,, as for UnsafeMutableRawBufferPointer.copy(from:bytes:), I cannot
find such a function anywhere in the API. There is copyBytes(from:)
<https://developer.apple.com/documentation/swift/unsafemutablerawbufferpointer/2635415-copybytes&gt;,
but the documentation is messed up and mentions a nonexistent count: argument
over and over again. The documentation also doesn’t mention what happens if
there is a length mismatch, so users are effectively relying on an
implementation detail. I don’t know how to best resolve this.

We currently have `UnsafeMutableRawBufferPointer.copyBytes(from:)`. I
don’t think your proposal changes that. The current docs refer to the
`source` parameter, which is correct. Docs refer to the parameter name, not
the label name. So `source.count` is the size of the input. I was pointing
out that it has the semantics: `debugAssert(source.count <= self.count)`.

Your proposal changes `UnsafeRawPointer.copyBytes(from:count:)` to
`UnsafeRawPointer.copy(from:bytes:)`. Originally we wanted to those API
names to match, but I’m fine with your change. What is more important is
that the semantics are the same as `copyBytes(from:)`. Furthermore, any new
methods that you add that copy into a raw buffer (e.g.
initializeMemory(as:from:count:)) should have similar behavior.

I’m fine with switching to taking the count from the source, though I
think taking the count from the destination is slightly better because
1) the use cases I mentioned in the other email, and 2) all the other
memorystate functions use self.count instead of source.count, if they
take a source argument. But being consistent with the raw pointer
version is more important.

If it’s copying from a buffer it should not take a count, if it’s copying
from a pointer it obviously needs to take a count. What I mean by the two
versions being named consistently is simply that they’re both named
`copyBytes`. That really isn’t important though. The overflow/underflow
semantics being consistent are important.

(Incidentally, the reason “bytes” needs to be in the somewhere name is
because this method isn’t capable of copying nontrivial values)

Should the methods that don’t deal with raw buffers also be modified to
use the source argument (i.e. UnsafeMutableBufferPointer.ini
tialize(from:))?

I’m not sure what you mean by this. It also allows the destination to be
larger than the source. Initializing from a sequence does not trap on
overflow because we can’t guarantee the size of the sequence ahead of time.
When I talk about consistent overflow/underflow semantics, I’m only talking
about initializing one unsafe buffer/pointer from another unsafe
buffer/pointer.

Also, was there a reason why UnsafeMutableRawBufferPoin
ter.copyBytes(from:) uses the source’s count instead of its own? Right
now this behavior is “technically” undocumented behavior (as the public
docs haven’t been updated) so if there was ever a time to change it, now
would be it.

Mainly because partial initialization is more expected than dropping data
on the floor. Ultimately, this should be whatever typical developers would
expect the behavior to be. I would be very hesitant to change the behavior
now though.

-Andy

The problem is I would expect to be able to safely call deinitialize() and
friends after calling initialize(from:). If Element is a class type and
initialize doesn’t fill the entire buffer range, calling deinitialize()
will crash. That being said, since copy(from:bytes:) and copyBytes(from:)
don’t do any initialization and have no direct counterparts in
UnsafeMutableBufferPointer, it’s okay if they have different behavior than
the other methods.

You astutely pointed out that the UnsafeMutableBufferPointer.deinitialize()
method is dangerous, and I asked you to add a warning to its comments.
However, given the danger, I think we need to justify adding the method to
begin with. Are there real use cases that greatly benefit from it?

The problem is I would expect to be able to safely call deinitialize() and friends after calling initialize(from:). If Element is a class type and initialize doesn’t fill the entire buffer range, calling deinitialize() will crash. That being said, since copy(from:bytes:) and copyBytes(from:) don’t do any initialization and have no direct counterparts in UnsafeMutableBufferPointer, it’s okay if they have different behavior than the other methods.

You astutely pointed out that the UnsafeMutableBufferPointer.deinitialize() method is dangerous, and I asked you to add a warning to its comments. However, given the danger, I think we need to justify adding the method to begin with. Are there real use cases that greatly benefit from it?

I agree that’s a problem, which is why i was iffy on supporting partial initialization to begin with. The use case is for things like growing collections where you have to periodically move to larger storage. However, deinitialize is no more dangerous than moveInitialize, assign(repeating:count:), or moveAssign; they all deinitialize at least one entire buffer. If deinitialize is to be omitted, so must a majority of the unsafe pointer API.

Here's an alternative. Impose the precondition(source.count == self.count) to the following UnsafeMutableBufferPointer convenience methods that you propose adding:

+++ func assign(from:UnsafeBufferPointer<Element>)
+++ func assign(from:UnsafeMutableBufferPointer<Element>)
+++ func moveAssign(from:UnsafeMutableBufferPointer<Element>)
+++ func moveInitialize(from:UnsafeMutableBufferPointer<Element>)
+++ func initialize(from:UnsafeBufferPointer<Element>)
+++ func initialize(from:UnsafeMutableBufferPointer<Element>)

I don't that introduces any behavior that is inconsistent with other methods. `copyBytes` is a totally different thing that only works on trivial types. The currently dominant use case for UnsafeBufferPointer, partially initialized backing store, does not need to use your new convenience methods. It can continue dropping down to pointer+count style initialization/deinitialization.

-Andy

The problem is I would expect to be able to safely call deinitialize()
and friends after calling initialize(from:). If Element is a class type and
initialize doesn’t fill the entire buffer range, calling deinitialize()
will crash. That being said, since copy(from:bytes:) and copyBytes(from:)
don’t do any initialization and have no direct counterparts in
UnsafeMutableBufferPointer, it’s okay if they have different behavior than
the other methods.

You astutely pointed out that the UnsafeMutableBufferPointer.deinitialize()
method is dangerous, and I asked you to add a warning to its comments.
However, given the danger, I think we need to justify adding the method to
begin with. Are there real use cases that greatly benefit from it?

I agree that’s a problem, which is why i was iffy on supporting partial
initialization to begin with. The use case is for things like growing
collections where you have to periodically move to larger storage. However,
deinitialize is no more dangerous than moveInitialize,
assign(repeating:count:), or moveAssign; they all deinitialize at least one
entire buffer. If deinitialize is to be omitted, so must a majority of the
unsafe pointer API.

Here's an alternative. Impose the precondition(source.count == self.count)
to the following UnsafeMutableBufferPointer convenience methods that you
propose adding:

+++ func assign(from:UnsafeBufferPointer<Element>)
+++ func assign(from:UnsafeMutableBufferPointer<Element>)
+++ func moveAssign(from:UnsafeMutableBufferPointer<Element>)
+++ func moveInitialize(from:UnsafeMutableBufferPointer<Element>)
+++ func initialize(from:UnsafeBufferPointer<Element>)
+++ func initialize(from:UnsafeMutableBufferPointer<Element>)

I don't that introduces any behavior that is inconsistent with other
methods. `copyBytes` is a totally different thing that only works on
trivial types. The currently dominant use case for UnsafeBufferPointer,
partially initialized backing store, does not need to use your new
convenience methods. It can continue dropping down to pointer+count style
initialization/deinitialization.

-Andy

the latest draft does not have assign(from:UnsafeMutableBufferPointer<

) or initialize(from:UnsafeMutableBufferPointer<Element>), it uses

the generic Sequence methods that are already there that do not require
that precondition.

···

On Sat, Aug 19, 2017 at 8:52 PM, Andrew Trick <atrick@apple.com> wrote:

Sorry, I was pasting from your original proposal. Here are the relevant methods from the latest draft:

+++ func moveInitialize(from:UnsafeMutableBufferPointer<Element>)
+++ func moveAssign(from:UnsafeMutableBufferPointer<Element>)

But with the precondition, the `assign` method could be reasonably added back, right?
+++ func assign(from:UnsafeMutableBufferPointer<Element>)

Likewise, I don’t have a problem with initialize(from: UnsafeBufferPointer) where self.count==source.count. The Sequence initializer is different. It’s designed for the Array use case and forces the caller to deal with partial initialization.

UnsafeMutableRawBufferPointer.moveInitializeMemory on the other hand probably doesn't need that precondition since there's no way to deinitialize. It just needs clear comments.

-Andy

···

On Aug 19, 2017, at 6:03 PM, Taylor Swift <kelvin13ma@gmail.com> wrote:

On Sat, Aug 19, 2017 at 8:52 PM, Andrew Trick <atrick@apple.com <mailto:atrick@apple.com>> wrote:

The problem is I would expect to be able to safely call deinitialize() and friends after calling initialize(from:). If Element is a class type and initialize doesn’t fill the entire buffer range, calling deinitialize() will crash. That being said, since copy(from:bytes:) and copyBytes(from:) don’t do any initialization and have no direct counterparts in UnsafeMutableBufferPointer, it’s okay if they have different behavior than the other methods.

You astutely pointed out that the UnsafeMutableBufferPointer.deinitialize() method is dangerous, and I asked you to add a warning to its comments. However, given the danger, I think we need to justify adding the method to begin with. Are there real use cases that greatly benefit from it?

I agree that’s a problem, which is why i was iffy on supporting partial initialization to begin with. The use case is for things like growing collections where you have to periodically move to larger storage. However, deinitialize is no more dangerous than moveInitialize, assign(repeating:count:), or moveAssign; they all deinitialize at least one entire buffer. If deinitialize is to be omitted, so must a majority of the unsafe pointer API.

Here's an alternative. Impose the precondition(source.count == self.count) to the following UnsafeMutableBufferPointer convenience methods that you propose adding:

+++ func assign(from:UnsafeBufferPointer<Element>)
+++ func assign(from:UnsafeMutableBufferPointer<Element>)
+++ func moveAssign(from:UnsafeMutableBufferPointer<Element>)
+++ func moveInitialize(from:UnsafeMutableBufferPointer<Element>)
+++ func initialize(from:UnsafeBufferPointer<Element>)
+++ func initialize(from:UnsafeMutableBufferPointer<Element>)

I don't that introduces any behavior that is inconsistent with other methods. `copyBytes` is a totally different thing that only works on trivial types. The currently dominant use case for UnsafeBufferPointer, partially initialized backing store, does not need to use your new convenience methods. It can continue dropping down to pointer+count style initialization/deinitialization.

-Andy

the latest draft does not have assign(from:UnsafeMutableBufferPointer<Element>) or initialize(from:UnsafeMutableBufferPointer<Element>), it uses the generic Sequence methods that are already there that do not require that precondition.

What you’re describing is basically an earlier version of the proposal
which had a slightly weaker precondition (source >= destination) than yours
(source == destination). That one basically ignored the Sequence methods at
the expense of greater API surface area.

···

On Sat, Aug 19, 2017 at 9:08 PM, Andrew Trick <atrick@apple.com> wrote:

On Aug 19, 2017, at 6:03 PM, Taylor Swift <kelvin13ma@gmail.com> wrote:

On Sat, Aug 19, 2017 at 8:52 PM, Andrew Trick <atrick@apple.com> wrote:

The problem is I would expect to be able to safely call deinitialize()
and friends after calling initialize(from:). If Element is a class type and
initialize doesn’t fill the entire buffer range, calling deinitialize()
will crash. That being said, since copy(from:bytes:) and copyBytes(from:)
don’t do any initialization and have no direct counterparts in
UnsafeMutableBufferPointer, it’s okay if they have different behavior than
the other methods.

You astutely pointed out that the UnsafeMutableBufferPointer.deinitialize()
method is dangerous, and I asked you to add a warning to its comments.
However, given the danger, I think we need to justify adding the method to
begin with. Are there real use cases that greatly benefit from it?

I agree that’s a problem, which is why i was iffy on supporting partial
initialization to begin with. The use case is for things like growing
collections where you have to periodically move to larger storage. However,
deinitialize is no more dangerous than moveInitialize,
assign(repeating:count:), or moveAssign; they all deinitialize at least one
entire buffer. If deinitialize is to be omitted, so must a majority of the
unsafe pointer API.

Here's an alternative. Impose the precondition(source.count ==
self.count) to the following UnsafeMutableBufferPointer convenience methods
that you propose adding:

+++ func assign(from:UnsafeBufferPointer<Element>)
+++ func assign(from:UnsafeMutableBufferPointer<Element>)
+++ func moveAssign(from:UnsafeMutableBufferPointer<Element>)
+++ func moveInitialize(from:UnsafeMutableBufferPointer<Element>)
+++ func initialize(from:UnsafeBufferPointer<Element>)
+++ func initialize(from:UnsafeMutableBufferPointer<Element>)

I don't that introduces any behavior that is inconsistent with other
methods. `copyBytes` is a totally different thing that only works on
trivial types. The currently dominant use case for UnsafeBufferPointer,
partially initialized backing store, does not need to use your new
convenience methods. It can continue dropping down to pointer+count style
initialization/deinitialization.

-Andy

the latest draft does not have assign(from:UnsafeMutableBufferPointer<
>) or initialize(from:UnsafeMutableBufferPointer<Element>), it
uses the generic Sequence methods that are already there that do not
require that precondition.

Sorry, I was pasting from your original proposal. Here are the relevant
methods from the latest draft:

https://github.com/kelvin13/swift-evolution/blob/
1b7738513c00388b8de3b09769eab773539be386/proposals/0184-
improved-pointers.md

+++ func moveInitialize(from:UnsafeMutableBufferPointer<Element>)
+++ func moveAssign(from:UnsafeMutableBufferPointer<Element>)

But with the precondition, the `assign` method could be reasonably added
back, right?
+++ func assign(from:UnsafeMutableBufferPointer<Element>)

Likewise, I don’t have a problem with initialize(from:
UnsafeBufferPointer) where self.count==source.count. The Sequence
initializer is different. It’s designed for the Array use case and forces
the caller to deal with partial initialization.

UnsafeMutableRawBufferPointer.moveInitializeMemory on the other hand
probably doesn't need that precondition since there's no way to
deinitialize. It just needs clear comments.

-Andy

What you’re describing is basically an earlier version of the proposal which had a slightly weaker precondition (source >= destination) than yours (source == destination). That one basically ignored the Sequence methods at the expense of greater API surface area.

The Sequence methods don’t provide the simpler, more convenient form of initialization/deinitialization that I thought you wanted. I see two reasonable options.

1. Don’t provide any new buffer initialization/deinitialization convenience. i.e. drop UsafeMutableBufferPointer moveInitialize, moveAssign, and deinitialize from your proposal.

2. Provide the full set of convenience methods: initialize, assign, moveInitialize, and moveAssign assuming self.count==source.count. And provide deinitialize() to be used only in conjunction with those new initializers.

The question is really whether those new methods are going to significantly simplify your code. If not, #1 is the conservative choice. Don't provide convenience which could be misused. Put off solving that problem until we can design a new move-only buffer type that tracks partially initialized state.

-Andy

···

On Aug 19, 2017, at 6:16 PM, Taylor Swift <kelvin13ma@gmail.com> wrote:

On Sat, Aug 19, 2017 at 9:08 PM, Andrew Trick <atrick@apple.com <mailto:atrick@apple.com>> wrote:

On Aug 19, 2017, at 6:03 PM, Taylor Swift <kelvin13ma@gmail.com <mailto:kelvin13ma@gmail.com>> wrote:

On Sat, Aug 19, 2017 at 8:52 PM, Andrew Trick <atrick@apple.com <mailto:atrick@apple.com>> wrote:

The problem is I would expect to be able to safely call deinitialize() and friends after calling initialize(from:). If Element is a class type and initialize doesn’t fill the entire buffer range, calling deinitialize() will crash. That being said, since copy(from:bytes:) and copyBytes(from:) don’t do any initialization and have no direct counterparts in UnsafeMutableBufferPointer, it’s okay if they have different behavior than the other methods.

You astutely pointed out that the UnsafeMutableBufferPointer.deinitialize() method is dangerous, and I asked you to add a warning to its comments. However, given the danger, I think we need to justify adding the method to begin with. Are there real use cases that greatly benefit from it?

I agree that’s a problem, which is why i was iffy on supporting partial initialization to begin with. The use case is for things like growing collections where you have to periodically move to larger storage. However, deinitialize is no more dangerous than moveInitialize, assign(repeating:count:), or moveAssign; they all deinitialize at least one entire buffer. If deinitialize is to be omitted, so must a majority of the unsafe pointer API.

Here's an alternative. Impose the precondition(source.count == self.count) to the following UnsafeMutableBufferPointer convenience methods that you propose adding:

+++ func assign(from:UnsafeBufferPointer<Element>)
+++ func assign(from:UnsafeMutableBufferPointer<Element>)
+++ func moveAssign(from:UnsafeMutableBufferPointer<Element>)
+++ func moveInitialize(from:UnsafeMutableBufferPointer<Element>)
+++ func initialize(from:UnsafeBufferPointer<Element>)
+++ func initialize(from:UnsafeMutableBufferPointer<Element>)

I don't that introduces any behavior that is inconsistent with other methods. `copyBytes` is a totally different thing that only works on trivial types. The currently dominant use case for UnsafeBufferPointer, partially initialized backing store, does not need to use your new convenience methods. It can continue dropping down to pointer+count style initialization/deinitialization.

-Andy

the latest draft does not have assign(from:UnsafeMutableBufferPointer<Element>) or initialize(from:UnsafeMutableBufferPointer<Element>), it uses the generic Sequence methods that are already there that do not require that precondition.

Sorry, I was pasting from your original proposal. Here are the relevant methods from the latest draft:

https://github.com/kelvin13/swift-evolution/blob/1b7738513c00388b8de3b09769eab773539be386/proposals/0184-improved-pointers.md

+++ func moveInitialize(from:UnsafeMutableBufferPointer<Element>)
+++ func moveAssign(from:UnsafeMutableBufferPointer<Element>)

But with the precondition, the `assign` method could be reasonably added back, right?
+++ func assign(from:UnsafeMutableBufferPointer<Element>)

Likewise, I don’t have a problem with initialize(from: UnsafeBufferPointer) where self.count==source.count. The Sequence initializer is different. It’s designed for the Array use case and forces the caller to deal with partial initialization.

UnsafeMutableRawBufferPointer.moveInitializeMemory on the other hand probably doesn't need that precondition since there's no way to deinitialize. It just needs clear comments.

-Andy

I’m not sure the answer is to just omit methods from
UnsafeMutableBufferPointer since most of the original complaints circulated
around having to un-nil baseAddress to do anything with them.

What if only unary methods should be added to UnsafeMutableBufferPointer
without count:, meaning:

initialize(repeating:)
assign(repeating:)
deinitialize()

and the other methods should take both an *offset* parameter instead of a
count parameter:

initialize(from:at:)
assign(from:at:)
moveInitialize(from:at:)
moveAssign(from:at:)

which provides maximum explicitness. This requires improvements to buffer
pointer slicing though. But I’m not a fan of the mission creep that’s
working into this proposal (i only originally wrote the thing to get
allocate(capacity:) and deallocate() into UnsafeMutableBufferPointer!)

···

On Sat, Aug 19, 2017 at 9:31 PM, Andrew Trick <atrick@apple.com> wrote:

On Aug 19, 2017, at 6:16 PM, Taylor Swift <kelvin13ma@gmail.com> wrote:

What you’re describing is basically an earlier version of the proposal
which had a slightly weaker precondition (source >= destination) than yours
(source == destination). That one basically ignored the Sequence methods at
the expense of greater API surface area.

The Sequence methods don’t provide the simpler, more convenient form of
initialization/deinitialization that I thought you wanted. I see two
reasonable options.

1. Don’t provide any new buffer initialization/deinitialization
convenience. i.e. drop UsafeMutableBufferPointer moveInitialize,
moveAssign, and deinitialize from your proposal.

2. Provide the full set of convenience methods: initialize, assign,
moveInitialize, and moveAssign assuming self.count==source.count. And
provide deinitialize() to be used only in conjunction with those new
initializers.

The question is really whether those new methods are going to
significantly simplify your code. If not, #1 is the conservative choice.
Don't provide convenience which could be misused. Put off solving that
problem until we can design a new move-only buffer type that tracks
partially initialized state.

-Andy

What you’re describing is basically an earlier version of the proposal
which had a slightly weaker precondition (source >= destination) than yours
(source == destination). That one basically ignored the Sequence methods at
the expense of greater API surface area.

The Sequence methods don’t provide the simpler, more convenient form of
initialization/deinitialization that I thought you wanted. I see two
reasonable options.

1. Don’t provide any new buffer initialization/deinitialization
convenience. i.e. drop UsafeMutableBufferPointer moveInitialize,
moveAssign, and deinitialize from your proposal.

2. Provide the full set of convenience methods: initialize, assign,
moveInitialize, and moveAssign assuming self.count==source.count. And
provide deinitialize() to be used only in conjunction with those new
initializers.

The question is really whether those new methods are going to
significantly simplify your code. If not, #1 is the conservative choice.
Don't provide convenience which could be misused. Put off solving that
problem until we can design a new move-only buffer type that tracks
partially initialized state.

-Andy

I’m not sure the answer is to just omit methods from
UnsafeMutableBufferPointer since most of the original complaints
circulated around having to un-nil baseAddress to do anything with them.

I know un-nil’ing baseAddress is horrible, but I don’t think working
around that is an important goal yet. Eventually there will be a much
safer, more convenient mechanism for manual allocation that doesn’t involve
“pointers". I also considered adding API surface to
UnsafeMutableBufferPointer.Slice, but that’s beyond what we should do now
and may also become irrelevant when we have a more sophisticated buffer
type.

What if only unary methods should be added to UnsafeMutableBufferPointer
without count:, meaning:

initialize(repeating:)

I actually have no problem with this one... except that it could be
confused with UnsafeMutablePointer.initialize(repeating:), but I’ll
ignore that since we already discussed it.

assign(repeating:)
deinitialize()

These are fine only if we have use cases that warrant them AND those use
cases are expected to fully initialize the buffer, either via
initialize(repeating:) or initialize(from: buffer) with
precondition(source.count==self.count). They don’t really make sense for
the use case that I’m familiar with. Without clear motivating code
patterns, they aren’t worth the risk. “API Completeness” doesn’t have
intrinsic value.

An example use for assign(repeating:) would be to zero out an image buffer.

and the other methods should take both an *offset* parameter instead of a
count parameter:

initialize(from:at:)
assign(from:at:)
moveInitialize(from:at:)
moveAssign(from:at:)

which provides maximum explicitness. This requires improvements to buffer
pointer slicing though. But I’m not a fan of the mission creep that’s
working into this proposal (i only originally wrote the thing to get
allocate(capacity:) and deallocate() into UnsafeMutableBufferPointer!)

I’m open to that, with source.count <= self.count + index. They are
potentially ambiguous (the `at` could refer to a source index) but
consistent with the idea that this API is for copying an entire source
buffer into a slice of the destination buffer. Again, we need to find real
code that benefits from this, but I expect the stdlib could use these.

-Andy

The more I think the more I believe using from:at: is the right approach.
The only problem is that it would have to be written as a generic on
Collection or Sequence to avoid having to provide up to 4 overloads for
each operation, since we would want these to work well with buffer slices
as well as buffers themselves. That puts them uncomfortably close to the
turf of the existing buffer pointer Sequence API though.

Or we could make UnsafeMutableBufferPointer its own slice type. Right now
MutableRandomAccessSlice<UnsafeMutableBufferPointer<Element>> takes up 4
words of storage when it really only needs two.

···

On Sat, Aug 19, 2017 at 10:28 PM, Andrew Trick <atrick@apple.com> wrote:

On Aug 19, 2017, at 6:42 PM, Taylor Swift <kelvin13ma@gmail.com> wrote:
On Sat, Aug 19, 2017 at 9:31 PM, Andrew Trick <atrick@apple.com> wrote:

On Aug 19, 2017, at 6:16 PM, Taylor Swift <kelvin13ma@gmail.com> wrote:

What you’re describing is basically an earlier version of the proposal which had a slightly weaker precondition (source >= destination) than yours (source == destination). That one basically ignored the Sequence methods at the expense of greater API surface area.

The Sequence methods don’t provide the simpler, more convenient form of initialization/deinitialization that I thought you wanted. I see two reasonable options.

1. Don’t provide any new buffer initialization/deinitialization convenience. i.e. drop UsafeMutableBufferPointer moveInitialize, moveAssign, and deinitialize from your proposal.

2. Provide the full set of convenience methods: initialize, assign, moveInitialize, and moveAssign assuming self.count==source.count. And provide deinitialize() to be used only in conjunction with those new initializers.

The question is really whether those new methods are going to significantly simplify your code. If not, #1 is the conservative choice. Don't provide convenience which could be misused. Put off solving that problem until we can design a new move-only buffer type that tracks partially initialized state.

-Andy

I’m not sure the answer is to just omit methods from UnsafeMutableBufferPointer since most of the original complaints circulated around having to un-nil baseAddress to do anything with them.

I know un-nil’ing baseAddress is horrible, but I don’t think working around that is an important goal yet. Eventually there will be a much safer, more convenient mechanism for manual allocation that doesn’t involve “pointers". I also considered adding API surface to UnsafeMutableBufferPointer.Slice, but that’s beyond what we should do now and may also become irrelevant when we have a more sophisticated buffer type.

What if only unary methods should be added to UnsafeMutableBufferPointer without count:, meaning:

initialize(repeating:)

I actually have no problem with this one... except that it could be confused with UnsafeMutablePointer.initialize(repeating:), but I’ll ignore that since we already discussed it.

assign(repeating:)
deinitialize()

These are fine only if we have use cases that warrant them AND those use cases are expected to fully initialize the buffer, either via initialize(repeating:) or initialize(from: buffer) with precondition(source.count==self.count). They don’t really make sense for the use case that I’m familiar with. Without clear motivating code patterns, they aren’t worth the risk. “API Completeness” doesn’t have intrinsic value.

and the other methods should take both an offset parameter instead of a count parameter:

initialize(from:at:)
assign(from:at:)
moveInitialize(from:at:)
moveAssign(from:at:)

which provides maximum explicitness. This requires improvements to buffer pointer slicing though. But I’m not a fan of the mission creep that’s working into this proposal (i only originally wrote the thing to get allocate(capacity:) and deallocate() into UnsafeMutableBufferPointer!)

I’m open to that, with source.count <= self.count + index. They are potentially ambiguous (the `at` could refer to a source index) but consistent with the idea that this API is for copying an entire source buffer into a slice of the destination buffer. Again, we need to find real code that benefits from this, but I expect the stdlib could use these.

-Andy

···

On Aug 19, 2017, at 6:42 PM, Taylor Swift <kelvin13ma@gmail.com> wrote:
On Sat, Aug 19, 2017 at 9:31 PM, Andrew Trick <atrick@apple.com <mailto:atrick@apple.com>> wrote:

On Aug 19, 2017, at 6:16 PM, Taylor Swift <kelvin13ma@gmail.com <mailto:kelvin13ma@gmail.com>> wrote:

How disruptive would it be to move the `at:` parameter in `
UnsafeMutableRawPointer.initializeMemory(as:at:count:to:)` to a method in `
UnsafeMutableRawBufferPointer`?

(raw_ptr + 16).initializeMemory(as: UInt64.self, from: source, count: 5)

instead of

raw_ptr.initializeMemory(as: UInt64.self, at: 2, from: source, count: 5)

···

On Sun, Aug 20, 2017 at 12:12 AM, Taylor Swift via swift-evolution < swift-evolution@swift.org> wrote:

On Sat, Aug 19, 2017 at 10:28 PM, Andrew Trick <atrick@apple.com> wrote:

On Aug 19, 2017, at 6:42 PM, Taylor Swift <kelvin13ma@gmail.com> wrote:

On Sat, Aug 19, 2017 at 9:31 PM, Andrew Trick <atrick@apple.com> wrote:

On Aug 19, 2017, at 6:16 PM, Taylor Swift <kelvin13ma@gmail.com> wrote:

What you’re describing is basically an earlier version of the proposal
which had a slightly weaker precondition (source >= destination) than yours
(source == destination). That one basically ignored the Sequence methods at
the expense of greater API surface area.

The Sequence methods don’t provide the simpler, more convenient form of
initialization/deinitialization that I thought you wanted. I see two
reasonable options.

1. Don’t provide any new buffer initialization/deinitialization
convenience. i.e. drop UsafeMutableBufferPointer moveInitialize,
moveAssign, and deinitialize from your proposal.

2. Provide the full set of convenience methods: initialize, assign,
moveInitialize, and moveAssign assuming self.count==source.count. And
provide deinitialize() to be used only in conjunction with those new
initializers.

The question is really whether those new methods are going to
significantly simplify your code. If not, #1 is the conservative choice.
Don't provide convenience which could be misused. Put off solving that
problem until we can design a new move-only buffer type that tracks
partially initialized state.

-Andy

I’m not sure the answer is to just omit methods from
UnsafeMutableBufferPointer since most of the original complaints
circulated around having to un-nil baseAddress to do anything with them.

I know un-nil’ing baseAddress is horrible, but I don’t think working
around that is an important goal yet. Eventually there will be a much
safer, more convenient mechanism for manual allocation that doesn’t involve
“pointers". I also considered adding API surface to
UnsafeMutableBufferPointer.Slice, but that’s beyond what we should do
now and may also become irrelevant when we have a more sophisticated buffer
type.

What if only unary methods should be added to UnsafeMutableBufferPointer
without count:, meaning:

initialize(repeating:)

I actually have no problem with this one... except that it could be
confused with UnsafeMutablePointer.initialize(repeating:), but I’ll
ignore that since we already discussed it.

assign(repeating:)
deinitialize()

These are fine only if we have use cases that warrant them AND those use
cases are expected to fully initialize the buffer, either via
initialize(repeating:) or initialize(from: buffer) with
precondition(source.count==self.count). They don’t really make sense for
the use case that I’m familiar with. Without clear motivating code
patterns, they aren’t worth the risk. “API Completeness” doesn’t have
intrinsic value.

An example use for assign(repeating:) would be to zero out an image buffer.

and the other methods should take both an *offset* parameter instead of
a count parameter:

initialize(from:at:)
assign(from:at:)
moveInitialize(from:at:)
moveAssign(from:at:)

which provides maximum explicitness. This requires improvements to buffer
pointer slicing though. But I’m not a fan of the mission creep that’s
working into this proposal (i only originally wrote the thing to get
allocate(capacity:) and deallocate() into UnsafeMutableBufferPointer!)

I’m open to that, with source.count <= self.count + index. They are
potentially ambiguous (the `at` could refer to a source index) but
consistent with the idea that this API is for copying an entire source
buffer into a slice of the destination buffer. Again, we need to find real
code that benefits from this, but I expect the stdlib could use these.

-Andy

The more I think the more I believe using from:at: is the right approach.
The only problem is that it would have to be written as a generic on
Collection or Sequence to avoid having to provide up to 4 overloads for
each operation, since we would want these to work well with buffer slices
as well as buffers themselves. That puts them uncomfortably close to the
turf of the existing buffer pointer Sequence API though.

Or we could make UnsafeMutableBufferPointer its own slice type. Right now
MutableRandomAccessSlice<UnsafeMutableBufferPointer<Element>> takes up 4
words of storage when it really only needs two.

_______________________________________________
swift-evolution mailing list
swift-evolution@swift.org
https://lists.swift.org/mailman/listinfo/swift-evolution

actually never mind that, UnsafeMutablePointer should be the only type to not support at: arguments since offsetting them is easy with +.

···

On Aug 20, 2017, at 12:12 AM, Taylor Swift via swift-evolution <swift-evolution@swift.org> wrote:

On Sat, Aug 19, 2017 at 10:28 PM, Andrew Trick <atrick@apple.com> wrote:

On Aug 19, 2017, at 6:42 PM, Taylor Swift <kelvin13ma@gmail.com> wrote:

On Sat, Aug 19, 2017 at 9:31 PM, Andrew Trick <atrick@apple.com> wrote:

On Aug 19, 2017, at 6:16 PM, Taylor Swift <kelvin13ma@gmail.com> wrote:

What you’re describing is basically an earlier version of the proposal which had a slightly weaker precondition (source >= destination) than yours (source == destination). That one basically ignored the Sequence methods at the expense of greater API surface area.

The Sequence methods don’t provide the simpler, more convenient form of initialization/deinitialization that I thought you wanted. I see two reasonable options.

1. Don’t provide any new buffer initialization/deinitialization convenience. i.e. drop UsafeMutableBufferPointer moveInitialize, moveAssign, and deinitialize from your proposal.

2. Provide the full set of convenience methods: initialize, assign, moveInitialize, and moveAssign assuming self.count==source.count. And provide deinitialize() to be used only in conjunction with those new initializers.

The question is really whether those new methods are going to significantly simplify your code. If not, #1 is the conservative choice. Don't provide convenience which could be misused. Put off solving that problem until we can design a new move-only buffer type that tracks partially initialized state.

-Andy

I’m not sure the answer is to just omit methods from UnsafeMutableBufferPointer since most of the original complaints circulated around having to un-nil baseAddress to do anything with them.

I know un-nil’ing baseAddress is horrible, but I don’t think working around that is an important goal yet. Eventually there will be a much safer, more convenient mechanism for manual allocation that doesn’t involve “pointers". I also considered adding API surface to UnsafeMutableBufferPointer.Slice, but that’s beyond what we should do now and may also become irrelevant when we have a more sophisticated buffer type.

What if only unary methods should be added to UnsafeMutableBufferPointer without count:, meaning:

initialize(repeating:)

I actually have no problem with this one... except that it could be confused with UnsafeMutablePointer.initialize(repeating:), but I’ll ignore that since we already discussed it.

assign(repeating:)
deinitialize()

These are fine only if we have use cases that warrant them AND those use cases are expected to fully initialize the buffer, either via initialize(repeating:) or initialize(from: buffer) with precondition(source.count==self.count). They don’t really make sense for the use case that I’m familiar with. Without clear motivating code patterns, they aren’t worth the risk. “API Completeness” doesn’t have intrinsic value.

An example use for assign(repeating:) would be to zero out an image buffer.

and the other methods should take both an offset parameter instead of a count parameter:

initialize(from:at:)
assign(from:at:)
moveInitialize(from:at:)
moveAssign(from:at:)

which provides maximum explicitness. This requires improvements to buffer pointer slicing though. But I’m not a fan of the mission creep that’s working into this proposal (i only originally wrote the thing to get allocate(capacity:) and deallocate() into UnsafeMutableBufferPointer!)

I’m open to that, with source.count <= self.count + index. They are potentially ambiguous (the `at` could refer to a source index) but consistent with the idea that this API is for copying an entire source buffer into a slice of the destination buffer. Again, we need to find real code that benefits from this, but I expect the stdlib could use these.

-Andy

The more I think the more I believe using from:at: is the right approach. The only problem is that it would have to be written as a generic on Collection or Sequence to avoid having to provide up to 4 overloads for each operation, since we would want these to work well with buffer slices as well as buffers themselves. That puts them uncomfortably close to the turf of the existing buffer pointer Sequence API though.

Or we could make UnsafeMutableBufferPointer its own slice type. Right now MutableRandomAccessSlice<UnsafeMutableBufferPointer<Element>> takes up 4 words of storage when it really only needs two.
_______________________________________________
swift-evolution mailing list
swift-evolution@swift.org
https://lists.swift.org/mailman/listinfo/swift-evolution

and the other methods should take both an offset parameter instead of a count parameter:

initialize(from:at:)
assign(from:at:)
moveInitialize(from:at:)
moveAssign(from:at:)

which provides maximum explicitness. This requires improvements to buffer pointer slicing though. But I’m not a fan of the mission creep that’s working into this proposal (i only originally wrote the thing to get allocate(capacity:) and deallocate() into UnsafeMutableBufferPointer!)

I’m open to that, with source.count <= self.count + index. They are potentially ambiguous (the `at` could refer to a source index) but consistent with the idea that this API is for copying an entire source buffer into a slice of the destination buffer. Again, we need to find real code that benefits from this, but I expect the stdlib could use these.

In case that typo wasn’t obvious, we actually want the precondition: `offset + source.count <= self.count` (your latest proposal draft is correct).

The more I think the more I believe using from:at: is the right approach. The only problem is that it would have to be written as a generic on Collectionor Sequence to avoid having to provide up to 4 overloads for each operation, since we would want these to work well with buffer slices as well as buffers themselves. That puts them uncomfortably close to the turf of the existing buffer pointer Sequence API though.

It would have to be a generic method taking a RandomAccessCollection. Calling that would rely on tricky type inference. For now, I prefer the explicitly typed API in your current proposal.

Or we could make UnsafeMutableBufferPointer its own slice type. Right now MutableRandomAccessSlice<UnsafeMutableBufferPointer<Element>> takes up 4 words of storage when it really only needs two.

A slice needs to be a separate type because of the indexing semantics:

Note that it's actually a feature that Subsequence refers back to its original buffer. If we cared enough, we could define a buffer slice type that exposes most of the buffer API. I just don't think we need to worry about that level of convenience yet, especially with you adding the `at:` labels. I'm fine if developers resort to `init(rebasing:)` in rare cases. Again, once we have a safe move-only or reference counted buffer type, that will be the better choice for Swift APIs. For now, this is still largely driven by C interop.

I like this proposal as it stands!

-Andy

···

On Aug 19, 2017, at 9:12 PM, Taylor Swift <kelvin13ma@gmail.com> wrote:

New draft of the proposal is up here: <

Important changes start here
<https://github.com/kelvin13/swift-evolution/blob/patch-3/proposals/0184-improved-pointers.md#proposed-solution&gt;
.

···

On Sun, Aug 20, 2017 at 1:40 PM, Kelvin Ma <kelvin13ma@gmail.com> wrote:

actually never mind that, UnsafeMutablePointer should be the only type to
not support at: arguments since offsetting them is easy with +.

On Aug 20, 2017, at 12:12 AM, Taylor Swift via swift-evolution < > swift-evolution@swift.org> wrote:

On Sat, Aug 19, 2017 at 10:28 PM, Andrew Trick <atrick@apple.com> wrote:

On Aug 19, 2017, at 6:42 PM, Taylor Swift <kelvin13ma@gmail.com> wrote:

On Sat, Aug 19, 2017 at 9:31 PM, Andrew Trick <atrick@apple.com> wrote:

On Aug 19, 2017, at 6:16 PM, Taylor Swift <kelvin13ma@gmail.com> wrote:

What you’re describing is basically an earlier version of the proposal
which had a slightly weaker precondition (source >= destination) than yours
(source == destination). That one basically ignored the Sequence methods at
the expense of greater API surface area.

The Sequence methods don’t provide the simpler, more convenient form of
initialization/deinitialization that I thought you wanted. I see two
reasonable options.

1. Don’t provide any new buffer initialization/deinitialization
convenience. i.e. drop UsafeMutableBufferPointer moveInitialize,
moveAssign, and deinitialize from your proposal.

2. Provide the full set of convenience methods: initialize, assign,
moveInitialize, and moveAssign assuming self.count==source.count. And
provide deinitialize() to be used only in conjunction with those new
initializers.

The question is really whether those new methods are going to
significantly simplify your code. If not, #1 is the conservative choice.
Don't provide convenience which could be misused. Put off solving that
problem until we can design a new move-only buffer type that tracks
partially initialized state.

-Andy

I’m not sure the answer is to just omit methods from
UnsafeMutableBufferPointer since most of the original complaints
circulated around having to un-nil baseAddress to do anything with them.

I know un-nil’ing baseAddress is horrible, but I don’t think working
around that is an important goal yet. Eventually there will be a much
safer, more convenient mechanism for manual allocation that doesn’t involve
“pointers". I also considered adding API surface to
UnsafeMutableBufferPointer.Slice, but that’s beyond what we should do
now and may also become irrelevant when we have a more sophisticated buffer
type.

What if only unary methods should be added to UnsafeMutableBufferPointer
without count:, meaning:

initialize(repeating:)

I actually have no problem with this one... except that it could be
confused with UnsafeMutablePointer.initialize(repeating:), but I’ll
ignore that since we already discussed it.

assign(repeating:)
deinitialize()

These are fine only if we have use cases that warrant them AND those use
cases are expected to fully initialize the buffer, either via
initialize(repeating:) or initialize(from: buffer) with
precondition(source.count==self.count). They don’t really make sense for
the use case that I’m familiar with. Without clear motivating code
patterns, they aren’t worth the risk. “API Completeness” doesn’t have
intrinsic value.

An example use for assign(repeating:) would be to zero out an image buffer.

and the other methods should take both an *offset* parameter instead of
a count parameter:

initialize(from:at:)
assign(from:at:)
moveInitialize(from:at:)
moveAssign(from:at:)

which provides maximum explicitness. This requires improvements to buffer
pointer slicing though. But I’m not a fan of the mission creep that’s
working into this proposal (i only originally wrote the thing to get
allocate(capacity:) and deallocate() into UnsafeMutableBufferPointer!)

I’m open to that, with source.count <= self.count + index. They are
potentially ambiguous (the `at` could refer to a source index) but
consistent with the idea that this API is for copying an entire source
buffer into a slice of the destination buffer. Again, we need to find real
code that benefits from this, but I expect the stdlib could use these.

-Andy

The more I think the more I believe using from:at: is the right approach.
The only problem is that it would have to be written as a generic on
Collection or Sequence to avoid having to provide up to 4 overloads for
each operation, since we would want these to work well with buffer slices
as well as buffers themselves. That puts them uncomfortably close to the
turf of the existing buffer pointer Sequence API though.

Or we could make UnsafeMutableBufferPointer its own slice type. Right now
MutableRandomAccessSlice<UnsafeMutableBufferPointer<Element>> takes up 4
words of storage when it really only needs two.

_______________________________________________
swift-evolution mailing list
swift-evolution@swift.org
https://lists.swift.org/mailman/listinfo/swift-evolution

and the other methods should take both an *offset* parameter instead of
a count parameter:

initialize(from:at:)
assign(from:at:)
moveInitialize(from:at:)
moveAssign(from:at:)

which provides maximum explicitness. This requires improvements to buffer
pointer slicing though. But I’m not a fan of the mission creep that’s
working into this proposal (i only originally wrote the thing to get
allocate(capacity:) and deallocate() into UnsafeMutableBufferPointer!)

I’m open to that, with source.count <= self.count + index. They are
potentially ambiguous (the `at` could refer to a source index) but
consistent with the idea that this API is for copying an entire source
buffer into a slice of the destination buffer. Again, we need to find real
code that benefits from this, but I expect the stdlib could use these.

In case that typo wasn’t obvious, we actually want the precondition:
`offset + source.count <= self.count` (your latest proposal draft is
correct).

The more I think the more I believe using from:at: is the right approach.
The only problem is that it would have to be written as a generic on
Collectionor Sequence to avoid having to provide up to 4 overloads for
each operation, since we would want these to work well with buffer slices
as well as buffers themselves. That puts them uncomfortably close to the
turf of the existing buffer pointer Sequence API though.

It would have to be a generic method taking a RandomAccessCollection.
Calling that would rely on tricky type inference. For now, I prefer the
explicitly typed API in your current proposal.

Or we could make UnsafeMutableBufferPointer its own slice type. Right now
MutableRandomAccessSlice<UnsafeMutableBufferPointer<Element>> takes up 4
words of storage when it really only needs two.

A slice needs to be a separate type because of the indexing semantics:
Process: Improvements to "How to propose a change" section (#2201) · apple/swift-evolution@8e32b67 · GitHub
c8165b41b188c3d095425a0b4636fcf299ee9036

Note that it's actually a feature that Subsequence refers back to its
original buffer. If we cared enough, we could define a buffer slice type
that exposes most of the buffer API. I just don't think we need to worry
about that level of convenience yet, especially with you adding the `at:`
labels. I'm fine if developers resort to `init(rebasing:)` in rare cases.
Again, once we have a safe move-only or reference counted buffer type, that
will be the better choice for Swift APIs. For now, this is still largely
driven by C interop.

a method like `buffer.segment(0 ..< 5)` or something which would avoid
constructing the slice type (and save typing that long
`UnsafeMutableBufferPointer(rebasing:)` name) like that would be nice . but
that’s just sugar and not very important if you ask me

I like this proposal as it stands!
https://github.com/kelvin13/swift-evolution/blob/
9d305ced7b82b9cf5854b55b3f0d08952853d046/proposals/0184-
improved-pointers.md

-Andy

I’ll try to get an updated implementation out in the next few days

···

On Sun, Aug 20, 2017 at 10:18 PM, Andrew Trick <atrick@apple.com> wrote:

On Aug 19, 2017, at 9:12 PM, Taylor Swift <kelvin13ma@gmail.com> wrote: