Skip to content

make mp_to_radix return the count of characters of the converted number #348

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

Merged

Conversation

czurnieden
Copy link
Contributor

As suggested by @sjaeckel in #343

Mmh…

@czurnieden
Copy link
Contributor Author

First problem is, as you can see, are the macros but that was expected and is, I think, solvable.
The problms with the MS-compiler are…interresting.

@czurnieden
Copy link
Contributor Author

I find it a bit awkward that way (there are alternatives which I find equally or even more odd, but YMMV, so drop a note, please).

A cleaner way, I think, would be to add another parameter, maybe

mp_err mp_to_radix(const mp_int *a, char *str, size_t maxlen, size_t *used_length, int radix)

or something in that line.

@sjaeckel
Copy link
Member

sjaeckel commented Sep 9, 2019

I find it a bit awkward that way

what do you find awkward?

What do you think about replacing mp_radix_size() in favor of mp_to_radix()?

  • len = 0; mp_to_radix(x, NULL, &len, radix); where len will then be filled with the approximated-to-be-allocated length... this wouldn't even need a separate error code...
  • len = 4567; mp_to_radix(x, buf, &len, radix); (where 4567 is enough) and len get's decreased to 4566 because the approximation overshot
  • an option would be len = 123; mp_to_radix(x, buf, &len, radix); (where 123 is too small to hold x) we would need a separate error code to tell the user to increase the buffer size

@minad
Copy link
Member

minad commented Sep 9, 2019

Removing radix_size is a good idea. We could still branch to an O(1) size computation function internally if buf==NULL.

@minad
Copy link
Member

minad commented Sep 9, 2019

The function in #343 could be used internally.

@minad
Copy link
Member

minad commented Sep 9, 2019

@sjaeckel what shall we do about the to_signed_bin functions? They also need a size argument. These functions should probably be treated differently since size computation is cheaper. But I don't like the difference in API now.

@sjaeckel
Copy link
Member

sjaeckel commented Sep 9, 2019

The function in #343 could be used internally.

I'd say either we drop the original one or we keep them both through a compile-time setting if someone doesn't want to spend the space for the tables

@sjaeckel what shall we do about the to_signed_bin functions? They also need a size argument. These functions should probably be treated differently since size computation is cheaper. But I don't like the difference in API now.

very good point! IMO we should deprecate the ones w/o length
... and probably can we then rename mp_to_unsigned_bin_n to mp_to_unsigned_bin for 2.0 (and provide a #define mp_to_unsigned_bin_n mp_to_unsigned_bin) ... not sure though ...

@minad
Copy link
Member

minad commented Sep 9, 2019

Just introduce functions with new names mp_to_ubin and mp_to_sbin?

@czurnieden
Copy link
Contributor Author

The function in #343 could be used internally.
I'd say either we drop the original one or we keep them both through a compile-time setting if someone doesn't want to spend the space for the tables

Tables has about 14k (depending a bit on MP_xBIT but not much), original about 3k , and the one with mp_ilogb has about 6k.
Oh, I though it would be more!

what do you find awkward?

My social interactions.
But making fun of myself aside:

What do you think about replacing mp_radix_size() in favor of mp_to_radix()?

That would be quite radical aaaaaand I like it! ;-)
*rolls up sleeves*

@minad
Copy link
Member

minad commented Sep 9, 2019

Hmm, I worked a bit on mp_to_radix and I am not sure if deprecating mp_radix_size is the right way.

We have the following scenarios:

  • str==NULL, *len=returns required length
  • str len too small, *len returns written chars or required length?
  • str len large enough, *len returns written chars

In particular the overloading in the case of buffer overflow is not nice. We could always return the needed length, but then the division would have to continue even if no chars are written anymore.
Generally, I think such overloading makes the library harder to use. It is non obvious anymore, by just looking at the functions. I have to check the documentation what the function does in which scenario etc.

My recommendation would be to not even merge this PR and just keep the length argument as value. The user can compute the number of written characters themselves. They know their buffer size and they know mp_radix_size. This doesn't sound userfriendly either, but I don't think this is a common use case. The standard use case is 1. calling mp_radix_size 2. allocating a buffer 2. calling mp_to_radix. The second use case is just calling mp_to_radix with a static buffer and hoping that the buffer is large enough (maybe falling back to allocating if not).

@minad minad self-requested a review September 9, 2019 18:00
@czurnieden
Copy link
Contributor Author

Hmm, I worked a bit on mp_to_radix and I am not sure if deprecating mp_radix_size is the right way.

The exact details are debatable, of course, but in general it is a good idea to get rid of it.

Since we got mp_ilogb the function mp_radix_size is reduced to not much more than a convenient little wrapper for mp_ilogb and every user who needs an exact size without actually printing the number can do it themselves, a note in the docs is more than sufficient here.

No to_string function I know needs to get given the size in advance, to the contrary, most of them return the number of characters needed. They only need a pointer and do the allocating themself, only freeing is left to the user. Used internally is in most cases a kind of log-function that overshoots by a couple and the actual buffer gets cut at the end as needed, if at all.
Although we cannot do it exactly in that way, it is not the worst solution, I think.

In particular the overloading in the case of buffer overflow is not nice.

I understood it in the way that that case is meant to replace mp_to_radix_n?
I still think that something like

mp_err mp_to_radix(const mp_int *a, char *str, size_t *length_written, size_t size, int radix) 

with

  • str == NULL -> FAIL because you cannot free that buffer after use anymore
  • length_written == NULL -> OK (if the caller doesn't want/need to know, who are we to insist?)
  • size == 0 -> OK, do the "computing size and allocating buffer" internally
  • size != 0 -> OK, do the "allocating buffer" internally and convert only the first size digits output (like the old mp_toradix_n).

would be better.
It would also be quite easy to the the old shortcut macros like mp_todecimal, mp_tohex etc.

@sjaeckel

But I don't like the difference in API now.

Could you be a bit more specific, please?

@minad
Copy link
Member

minad commented Sep 9, 2019

@czurnieden I think a function which allocates would also be acceptable, similar to what gmp offers.

But I don't like the difference in API now.
Could you be a bit more specific, please?

As of now mp_to_unsigned_bit, mp_unsigned_size, mp_to_radix, mp_radix_size etc all have a similar API. We should decide if we want to have the functions consistent. Should they all take buffers and allocate if no buffer is given? What shall they do if the buffer is not large enough?
Will we continue to offer mp_*_size functions?

@minad
Copy link
Member

minad commented Sep 9, 2019

EDIT: See updated proposal below

size_t mp_ubin_size(const mp_int *a) MP_WUR;
mp_err mp_from_ubin(mp_int *a, const unsigned char *buf, size_t size) MP_WUR;
mp_err mp_to_ubin(const mp_int *a, unsigned char *buf, size_t maxsize, size_t* written) MP_WUR;

size_t mp_sbin_size(const mp_int *a) MP_WUR;
mp_err mp_from_sbin(mp_int *a, const unsigned char *buf, size_t size) MP_WUR;
mp_err mp_to_sbin(const mp_int *a, unsigned char *buf, size_t maxsize, size_t* written) MP_WUR;

mp_err mp_radix_size(const mp_int *a, size_t *size, unsigned radix) MP_WUR;
mp_err mp_from_radix(mp_int *a, const char *str, unsigned radix) MP_WUR;
mp_err mp_to_radix(const mp_int *a, char *str, size_t maxsize, size_t* written, unsigned radix) MP_WUR;~

As @czurnieden proposed above, the mp_to_radix, mp_to_sbin and mp_to_ubin functions will allocate when str == NULL && size==0. str != NULL && size == 0 and str == NULL && size != 0 yield MP_VAL errors. Written bytes will be returned if written!=NULL.

@czurnieden czurnieden force-pushed the to_radix_returns_length_converted branch from 5e7f7c5 to fcaa30f Compare September 10, 2019 00:26
@czurnieden
Copy link
Contributor Author

czurnieden commented Sep 10, 2019

A quick try at the propositions. To be debated.
(*bin* not ignored, just not touched yet)

There is a realloc for now in mp_to_radix which can get removed if necessary.

@czurnieden
Copy link
Contributor Author

Took the liberty to add "work in progress" because that's what it is, isn't it?

@sjaeckel
Copy link
Member

sjaeckel commented Sep 10, 2019

I'm skeptic about the pattern-change from "alloc outside" to "alloc inside", but I somehow like it. I'd say if we do it like that we won't need the written arg anyways, right?

size_t mp_ubin_size(const mp_int *a) MP_WUR;
mp_err mp_from_ubin(mp_int *a, const unsigned char *buf, size_t size) MP_WUR;
mp_err mp_to_ubin(const mp_int *a, unsigned char **buf, size_t *len) MP_WUR;

size_t mp_sbin_size(const mp_int *a) MP_WUR;
mp_err mp_from_sbin(mp_int *a, const unsigned char *buf, size_t size) MP_WUR;
mp_err mp_to_sbin(const mp_int *a, unsigned char **buf, size_t *len) MP_WUR;

mp_err mp_radix_size(const mp_int *a, size_t *size, unsigned radix) MP_WUR;
mp_err mp_from_radix(mp_int *a, const char *str, unsigned radix) MP_WUR;
mp_err mp_to_radix(const mp_int *a, char **str, size_t *len, unsigned radix) MP_WUR;

where

  1. *len is always ignored as input
  2. we use the internal size-approximation
    2.a to do either the malloc if *str is NULL
    2.b or realloc *str to that size.
  3. update *len with the actual size

@minad
Copy link
Member

minad commented Sep 10, 2019

No realloc! This is a bad idea!

@sjaeckel
Copy link
Member

No realloc! This is a bad idea!

then no allocation inside IMO

@sjaeckel
Copy link
Member

No realloc! This is a bad idea!

then no allocation inside IMO

we can also do a free + malloc as we don't need the copy

@czurnieden
Copy link
Contributor Author

Did you manage to get this NOP issue fixed?

Fixed. It was my fault: a typo that I wasn't able to recognize for some reason, despite it—the typo, that is—screaming at me the whole time.
What did I do: well, instead of using the inner part MP_FOOBAR of the defining macro BN_MP_FOOBAR_C I C&P'd it from the end, so I had MP_FOOBAR_C.

Yes, I'm an idiot, sue me ;-)

czurnieden added a commit to czurnieden/libtommath that referenced this pull request Oct 1, 2019
czurnieden added a commit to czurnieden/libtommath that referenced this pull request Oct 1, 2019
@czurnieden czurnieden force-pushed the to_radix_returns_length_converted branch from 569b1ee to bd46b60 Compare October 1, 2019 14:36
@minad
Copy link
Member

minad commented Oct 2, 2019

Status? Could you please squash?

@czurnieden
Copy link
Contributor Author

We are only waiting for your OK, so if you are OK with it, drop me a note and I will squash&finish.
Yes, I C&P'ed ;-)

@czurnieden czurnieden force-pushed the to_radix_returns_length_converted branch from bd46b60 to ececff7 Compare October 2, 2019 21:28
@minad
Copy link
Member

minad commented Oct 3, 2019

Looks good

Copy link
Member

@sjaeckel sjaeckel left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

LGTM

@@ -6,7 +6,7 @@
/* reverse an array, used for radix code */
void s_mp_reverse(unsigned char *s, int len)
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

oh I just realized that it isn't done here... can you please change len to be size_t

@czurnieden czurnieden force-pushed the to_radix_returns_length_converted branch from b3fc059 to 0902fd2 Compare October 4, 2019 15:52
@minad minad self-requested a review October 5, 2019 15:21
@minad
Copy link
Member

minad commented Oct 5, 2019

Squash, rebase and fix CI? But besides that I approve.

@czurnieden
Copy link
Contributor Author

fix CI?

CI?

@czurnieden czurnieden force-pushed the to_radix_returns_length_converted branch from 0902fd2 to 8ba6283 Compare October 5, 2019 17:17
@czurnieden
Copy link
Contributor Author

@minad

fix CI?

Oh, you mean Travis (continuous integration)?
I cannot repair it, I don't work here.

Because I just cannot resist: it got a bit down here with the new owner, didn't it? >;->
*ducks&runs*

@czurnieden czurnieden force-pushed the to_radix_returns_length_converted branch from 8ba6283 to 1ceea2c Compare October 5, 2019 18:04
@czurnieden
Copy link
Contributor Author

I cannot repair it, I don't work here.

Ah, no that was over at #348
(I really shouldn't do that many PRs in parallel!)

@czurnieden czurnieden force-pushed the to_radix_returns_length_converted branch from 1ceea2c to e11b9c8 Compare October 6, 2019 01:37
Copy link
Member

@minad minad left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Looks good

@minad
Copy link
Member

minad commented Oct 6, 2019

@czurnieden thanks :) @sjaeckel from my side this looks ready.

@minad minad requested review from sjaeckel and removed request for fperrad October 6, 2019 21:14
@sjaeckel sjaeckel merged commit ce98f36 into libtom:develop Oct 7, 2019
@fperrad fperrad mentioned this pull request Oct 7, 2019
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

3 participants