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
Add sk_TYPE_new_reserve() function #4559
Conversation
@@ -31,7 +32,7 @@ sk_TYPE_dup, sk_TYPE_deep_copy, sk_TYPE_set_cmp_func - stack container | |||
TYPE *sk_TYPE_value(const STACK_OF(TYPE) *sk, int idx); | |||
STACK_OF(TYPE) *sk_TYPE_new(sk_TYPE_compfunc compare); | |||
STACK_OF(TYPE) *sk_TYPE_new_null(void); | |||
int sk_TYPE_reserve(STACK_OF(TYPE) *sk, size_t n); | |||
int sk_TYPE_reserve(STACK_OF(TYPE) *sk, int n); |
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?
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.
Because that's what OPENSSL_sk_reserve accepts. It's harmonization with code.
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.
Ah! Thanks for the reminder
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.
Internally the stack structure uses an int
for the number of elements present. This is exposed by the call sk_TYPE_num
which returns an int
. Changing to a size_t
makes sense to me but it is an API change.
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 fact, next time when API could be changed, IMHO the new
APIs of stack could be squashed into one, as something like sk_TYPE_new(int n, sk_TYPE_compfunc compare)
. If n == 0
, then it equals to sk_TYPW_new_null
...
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.
Just to clarify, this is wrong in the current documentation, and the documentation is fixed. Maybe you can move that fix to a different pull request so we can merge that to other branches?
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.
Or a separated commit inside this PR? and thus you can cherry-pick that commit...
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 works too of course
doc/man3/DEFINE_STACK_OF.pod
Outdated
@@ -53,6 +54,7 @@ sk_TYPE_dup, sk_TYPE_deep_copy, sk_TYPE_set_cmp_func - stack container | |||
sk_TYPE_freefunc freefunc); | |||
sk_TYPE_compfunc (*sk_TYPE_set_cmp_func(STACK_OF(TYPE) *sk, | |||
sk_TYPE_compfunc compare)); | |||
STACK_OF(TYPE) *sk_TYPE_new_reserve(int n); |
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 not size_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.
Same as above.
crypto/stack/stack.c
Outdated
if (!OPENSSL_sk_reserve(st, n)) { | ||
OPENSSL_sk_free(st); | ||
return NULL; | ||
} |
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.
Side note: in other places, we've used aggregate conditions, like this:
if ((st = OPENSSL_new_null()) == NULL
|| !OPENSSL_sk_reserve(st, n)) {
OPENSSL_sk_free(st);
return NULL;
}
Your choice if you want to do the same
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.
Okay, aggregate conditions work for me
doc/man3/DEFINE_STACK_OF.pod
Outdated
sk_TYPE_new_reserve() allocates a new stack with no comparison function, but | ||
additional memory in the new stack is allocated based on B<n>. Next B<n> calls | ||
to sk_TYPE_insert(), sk_TYPE_push() or sk_TYPE_unshift() will not fail or | ||
cause memory to be allocated or reallocated. |
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.
How about:
sk_TYPE_new_reserve() allocates a new empty stack with no comparison function. The new stack will have additional memory allocated to hold B elements. The next B calls to sk_TYPE_insert(), sk_TYPE_push() or sk_TYPE_unshift() will not fail or cause memory to be allocated or reallocated.
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, this is better
doc/man3/DEFINE_STACK_OF.pod
Outdated
@@ -213,6 +220,9 @@ index is out of range. | |||
sk_TYPE_new() and sk_TYPE_new_null() return an empty stack or B<NULL> if | |||
an error occurs. | |||
|
|||
sk_TYPE_new_reserve() returns a stack with allocation of the required | |||
memory or B<NULL> if an error occurs. |
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 this could be included with sk_TYPE_new rather than being spelt out separately. That there is additional memory allocated is mentioned in the description and isn't relevant to the returns section.
sk_TYPE_new(), sk_TYPE_new_null() and sk_TYPE_new_reserve() return an empty stack or B if an error occurs.
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.
Then you might need to indicate the difference of returned stack among those new
APIs, so how about:
sk_TYPE_new(), sk_TYPE_new_null() and sk_TYPE_new_reserve() return an empty stack with or without preallocated memory, or B if an error occurs.
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's okay too. The preallocation is mentioned above in the function description. I don't see a hard requirement for it to be mentioned again here.
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.
Hmmm, sounds reasonable.
Comments are addressed by the new commits... |
doc/man3/DEFINE_STACK_OF.pod
Outdated
@@ -101,6 +103,11 @@ or sk_TYPE_unshift() will not fail or cause memory to be allocated | |||
or reallocated. If B<n> is zero, any excess space allocated in the | |||
B<sk> structure is freed. On error B<sk> is unchanged. | |||
|
|||
sk_TYPE_new_reserve() allocates a new stack with no comparison function. | |||
The new stack will have additional memory allocated to hold B elements. |
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 that be B<n>
?
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.
right...
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.
Subject to the one comment.
d252b76
to
a334256
Compare
Fixed. The second commit should not be squashed |
Here is idea. How about "flat" structure. I mean instead of allocating |
I thought we outgrew that kind of stuff :) Less flippantly, don't we have to worry about alignment? IT's hard to get this kind of thing correct in portable code. It would be nice to avoid the fragmentation for fixed-sized stacks but I'm not sure the tradeoff is worth it. |
sizeof(structure) is always multiple of "largest" member type. This means that if there is a pointer member, then you can always "attach" array of pointers. |
I don't think that the aggregate structure @dot-asm proposes is a good idea. Consider if the stack needs to grow, how would you realloc the data? |
Yes, I think this ( |
A fixed-size stack is a glorified array, then you should really just have an array with an integer for growth index ;-) After all, the point with our stack implementation is the ability to grow, that's what makes it different from a simple array... |
Recall that reservations are for cases when caller actually knows size in advance. Yes, it's glorified array, but you have no choice, because callee wants it this way. As simplest mental exercise imagine function in question was called [As another mental exercise imagine you have subroutine adding two numbers, would you implement subroutine adding 42 to one number? I mean additional interfaces ought to add some value...] |
So imagine someone passing such a reserved stack to another unsuspecting function that wants to add more items. Generically speaking, that would be a bad thing, and I don't think that's an acceptable change of I'm not sure the gain is worth the possible cost. |
(also, I'm not sure what the gain really is. One less OPENSSL_zalloc()?) |
Less fragmentation, better cache locality, less contention on MT malloc lock... |
Another use-case for stack reservation would be: a user doesn't know the size in advance, but the user does know there must be at least some size in the stack (Though I don't know if this case is considered in the initial motivation of the design). For instance, taking RSA as an example, someone may create a stack and always reserve 2 slots to contain the primes when creating the RSA object, since there must be at least 2 primes there but at this stage he/she doesn't know how many additional primes will be there... (Though the real RSA-related code is not implemented in this way, but this just could be an example...) |
Then this may be not for such user.
Then user shouldn't create reserved stack. But use pair of new + reserve calls. But once again, it's an a) option. But there is b)... |
If we're going to give this function such different semantics, then it should have a different. Having "reserve" mean different things depending on exactly which function was called is hugely misleading.
|
I'm also against allocating the reserved slots as part of the structure. I've seen and written C++ code that reserved space in a std::vector after creation and then needed that to expand. This is the same situation. Do not, however, reserve an additional slot each time you add one item ;) |
As already mentioned, imagine that was called new_reserved [note additional "d"]. :-) |
Seriously, that's not different enough. |
new_immutable then. But let me put it this way. In the essence what I'm trying to say is that additional public APIs ought to add real value, and mechanical combination of two calls doesn't really cut it. If aggregated/immutable is resenting, what would be more meaningful is to introduce subroutine that will work as replacement for both new flavours and reserve in the next major release... |
I do wonder why there are both I'm also a little dubious about the merit of the new_reserve call. I don't see any need for a new_immutable call. Create an array instead. I have thought about a call to make a stack immutable after creation. I.e. I'm done adding stuff to this and it is read only from here. |
To make the library more convenient to use is just the value here. Furthermore, if the And yes, there should be one new API to cover all functionality for current |
It has to be in next major release if we want to re-use name[s]. But if we add function with new name with intention to remove old names, then it can be done now. New name is actually more appropriate, because it's considered good style if old names hang around in intermediate major release. [Though it can as well be arranged with macros, i.e. you can compile program but there are no exported symbols.] Also note that sk_TYPE_reserve was just added, it wasn't released, so one can still replace it. To summarize, it would be more meaningful to design function that replaces all three, but keep original two till next major release, and discard sk_TYPE_reserve. [As for "flat" structure. Note that min_nodes is as low as 4. Each allocation has an overhead, and 4 pointers is somewhat wasteful. One can argue that initial minimum allocation can as well be flat...] |
a334256
to
c85c7fb
Compare
Fine. I changed |
You mean when you'd expect callee to make the reservation, right? I mean you want somebody else to have an opinion about reservation. Like callee would reserve place for additional elements it wants to add... Yeah, it makes sense... |
0c3ef78
to
234547d
Compare
new commits pushed... |
|
||
sk_TYPE_new_null() allocates a new empty stack with no comparison function. | ||
sk_TYPE_new_null() allocates a new empty stack with no comparison function. This | ||
function is equivalent to sk_TYPE_new_reserve(NULL, 0). | ||
|
||
sk_TYPE_reserve() allocates additional memory in the B<sk> structure | ||
such that the next B<n> calls to sk_TYPE_insert(), sk_TYPE_push() | ||
or sk_TYPE_unshift() will not fail or cause memory to be allocated | ||
or reallocated. If B<n> is zero, any excess space allocated in the | ||
B<sk> structure is freed. On error B<sk> is unchanged. |
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.
On related side note. Sentence before last one seems misleading. Excess space is apparently freed whenever current data + reservation is smaller than st.num_alloc. In other words it's not specific to case of n being zero. Ping @paulidale.
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, the excess space is freed in this circumstance too. However, the guarantee that the next n additions won't cause memory allocation is true.
I don't see a need to document this internal behaviour -- the stack will still function as expected. If this were documented, it would unnecessarily restrict any future re-implementation of the stack.
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 other words the sentence in question should be simply omitted, right?
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 so. PR #4582 is created to remove that sentence, to avoid 're-approval' of this PR...
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 removal is required. Being able to free up excess space seems like a reasonable thing to ask for, even without knowing anything about the internals of the stack data structure.
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.
Then the Note
section might be a better place to put this. In fact, I feel it would be better either to add a new standalone API to handle the 'free' job of the excess spaces to avoid misleading, e.g. OPENSSL_sk_shrink
...
(First time I saw OPENSSL_sk_reserve
and I thought if I passed a 0
of n
, it would do nothing, just as what malloc
-like functions do. But it's not...)
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.
One documentation nit about a missing the.
Approved once this is added.
doc/man3/DEFINE_STACK_OF.pod
Outdated
|
||
sk_TYPE_reserve() allocates additional memory in the B<sk> structure | ||
such that the next B<n> calls to sk_TYPE_insert(), sk_TYPE_push() | ||
or sk_TYPE_unshift() will not fail or cause memory to be allocated | ||
or reallocated. If B<n> is zero, any excess space allocated in the | ||
B<sk> structure is freed. On error B<sk> is unchanged. | ||
|
||
sk_TYPE_set_cmp_func() sets the comparison function of B<sk> to B<compar>. | ||
sk_TYPE_new_reserve() allocates a new stack. The new stack will have additional | ||
memory allocated to hold B<n> elements if B<n> is positive. Next B<n> calls to |
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.
The next B<n>
calls
This is a combination of sk_new and sk_reserve, to make it more convenient to allocate a new stack with reserved memory and comaprison function (if any).
<compar> to <compare> to match the var name in function prototype
234547d
to
86d6a4c
Compare
The 'The' is added... |
That sentence is very internal, as discussed in: openssl#4559 (comment)
Think of it as a hint to the data type that a number of adds are about to happen. We could live without it, but we could be more efficient with it. |
I can't merge this. I'm getting missing reviewed-by headers. |
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 this will help, @paulidale
This is a combination of sk_new and sk_reserve, to make it more convenient to allocate a new stack with reserved memory and comaprison function (if any). Reviewed-by: Paul Dale <paul.dale@oracle.com> Reviewed-by: Andy Polyakov <appro@openssl.org> Reviewed-by: Richard Levitte <levitte@openssl.org> (Merged from #4559)
Reviewed-by: Paul Dale <paul.dale@oracle.com> Reviewed-by: Andy Polyakov <appro@openssl.org> Reviewed-by: Richard Levitte <levitte@openssl.org> (Merged from #4559)
<compar> to <compare> to match the var name in function prototype Reviewed-by: Paul Dale <paul.dale@oracle.com> Reviewed-by: Andy Polyakov <appro@openssl.org> Reviewed-by: Richard Levitte <levitte@openssl.org> (Merged from #4559)
Yes, that fixed it. Merged thanks everyone. |
And someone can cherry-pick the doc-nits-fix commits to other branch now... |
doc-nits-fix backported to 1.1.0: 2e6d51d |
This is a combination of sk_new_null and sk_reserve, to make it more
convenient to allocate a new stack with reserved memory
Checklist