-
-
Notifications
You must be signed in to change notification settings - Fork 1.5k
Introduce View[T] type #5957
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
Introduce View[T] type #5957
Conversation
|
|
||
| proc newView*(s: string): ByteView = | ||
| ## Copies a string and returns a new view pointing into the copy. | ||
| let copied = new(string) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Why copy string? It could be shallowCopied instead. And no need for ref string variant, IMO
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I'm not sure. shallowCopy is counter-intuitive here. From system docs:
Be careful with the changed semantics though! There is a reason why the default assignment does a deep copy of sequences and strings.
Also: notice that ref string variant is not exported, because it's not safe when the string is resized.
|
This implementation looks completely unfriendly to JS |
|
Hmm, I guess JS needs separate implementation. At least fast C interop doesn't apply there. |
|
Have you thought about supporting the slicing notation? |
|
Good idea. Maybe I should just add |
|
@dom96 Do you think this can be merged? Any specific comments? |
|
@zielmicha, actually I've got a philosophical comment regarding the gcKeep field. I think in the very base impl of View there should be no |
| data: ptr T | ||
| length: int | ||
| when needGcKeep: | ||
| gcKeep: RootRef |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I think that you need both types, the type that keeps the underlying datastructure alive and one that doesn't. I expect a View object to be passed down the call stack, but it would not be written to some variable. So It would not need to keep the original object alive. The need for a gcKeep would be an exceptional case. But your implementation only allows to have it either globally enabled or disabled.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
needGcKeep is defined as not (compileOption("gc", "boehm") or compileOption("gc", "none")), so it can't be disabled even globally.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
yes that is what I mean. It can be disabled/enabled globally. But it should be possible to enable/disable it locally. Meaning a different type that has the RootRef and one that does not. Similar to ptr/ref being a different type. One does keep the object alive the other one doesn't.
| # View[char] and View[byte] are mostly the same thing | ||
| return initView(cast[ptr byte](s.data), s.len, when needGcKeep: s.gcKeep else: nil) | ||
|
|
||
| proc initView(s: ref string): ByteView = |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
both initView that accept string as well as seq only allow this, when the seq/string is a ref. To my knowledge it is not safe to cast a ptr to a ref, therefore it is not possible to "view" into a seq that is not ref type.
| return v.length | ||
|
|
||
| proc ptrAdd[T](p: pointer, i: int): ptr T = | ||
| return cast[ptr T](cast[int](p) +% (i * sizeof(T))) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
why don't you use a pointer to an unchecked array? That is a pointer with pointer arithmetics.
|
@yglukhov The only cost is that passing View as an argument copies 24 bytes, not 16. I'm not sure if introducing two types is worth this small saving. At least in my usecases, I haven't observed any performance difference at all between 24 and 16 byte View ( |
|
I would prefer the name Otherwise I am going to delegate this to @Araq, I'm mostly happy to include this though. |
|
|
||
| ByteView* = View[byte] | ||
|
|
||
| proc initView*[T; R: ref](data: ptr T, len: int, gcKeep: R): View[T] = |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I think that any function which accepts a 'ptr' should have an unsafe prefix.
| @@ -0,0 +1,148 @@ | |||
| ## View is a type representing a range of elements in an array. In can be thought as a pointer plus a size. | |||
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
In -> It
| ## View is a type representing a range of elements in an array. In can be thought as a pointer plus a size. | ||
| ## View can be created to an arbitrary memory segment and can additionally keep a single ``ref`` object alive. | ||
| ## | ||
| ## This module defines views and several helper operations on them. All functions in this module (except for unsafe version of ``initView``) are designed to be memory safe (e.g. they raise exception on out-of-bounds accesses). |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Examples in the docs would be nice.
| else: | ||
| initView(addr s[0], s[].len, gcKeep=s) | ||
|
|
||
| proc newView*[T](s: seq[T]): View[T] = |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This doesn't follow Nim's convention.
| ## Creates a view of length zero. | ||
| return initView[T](nil, 0) | ||
|
|
||
| proc initView[T](s: ref seq[T]): View[T] = |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Shouldn't this be var seq[T]?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
That wouldn't be memory safe. (You can use initView(ptr T, int) for that)
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Couldn't you use GC_ref to make it safe?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Yes. But I think then it needs to allocate object to make it possible to call GC_unref in destructor.
| result = initView(s) | ||
|
|
||
| proc isNil*(v: View): bool = | ||
| return v.len == 0 |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This is rather misleading.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Also, how can you use 'len' here when it's defined below this function?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
View is generic and len has multiple overloads, so it's mixin by default.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I would say, a View is never Nil, just empty and therefore this isNil should be removed.
|
Stylistic issues aside, it's mostly fine but I think it would be ill advised to introduce this. Once |
|
It's great the hear that the are plans to create first-class |
(rationale available as RFC #5753)
This PR adds
viewsmodule together withView[T]type. It is defined as a triple:initViewcreates view pointing into existing memory,newViewcreates view by copyingseqorstringcopyAsSeq,copyAsStringconvert the view to string/seqslicetakes a subview of a viewcopyFrom,copyTocopy data from views around[],[]=,iterator itemsare available