Skip to content
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

Faster float formatting #768

Open
wants to merge 1 commit into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
34 changes: 31 additions & 3 deletions ext/json/ext/generator/generator.c
Original file line number Diff line number Diff line change
Expand Up @@ -1050,12 +1050,15 @@ static void generate_json_integer(FBuffer *buffer, struct generate_json_data *da
}
#endif

#include "../vendor/fpconv/src/fpconv.c"

static void generate_json_float(FBuffer *buffer, struct generate_json_data *data, JSON_Generator_State *state, VALUE obj)
{
double value = RFLOAT_VALUE(obj);
char allow_nan = state->allow_nan;
if (!allow_nan) {
if (isinf(value) || isnan(value)) {
if (isinf(value) || isnan(value)) {
/* for NaN and Infinity values we either raise an error or rely on Float#to_s. */
if (!allow_nan) {
if (state->strict && state->as_json) {
VALUE casted_obj = rb_proc_call_with_block(state->as_json, 1, &obj, Qnil);
if (casted_obj != obj) {
Expand All @@ -1067,8 +1070,33 @@ static void generate_json_float(FBuffer *buffer, struct generate_json_data *data
}
raise_generator_error(obj, "%"PRIsVALUE" not allowed in JSON", rb_funcall(obj, i_to_s, 0));
}

VALUE tmp = rb_funcall(obj, i_to_s, 0);
fbuffer_append_str(buffer, tmp);
return;
}

/* This implementation writes directly into the buffer. We reserve
* the 24 characters that fpconv_dtoa states as its maximum, plus
* 2 more characters for the potential ".0" suffix.
*/
fbuffer_inc_capa(buffer, 26);
char* d = buffer->ptr + buffer->len;
int len = fpconv_dtoa(value, d);

/* fpconv_dtoa converts a float to its shorted string representation. When
* converting a float that is exactly an integer (e.g. `Float(2)`) this
* returns in a string that looks like an integer. This is correct, since
* JSON treats ints and floats the same. However, to not break integrations
* that expect a string representation looking like a float, we append a
* "." in that case.
*/
if(!memchr(d, '.', len) && !memchr(d, 'e', len)) {
d[len] = '.';
d[len+1] = '0';
len += 2;
Comment on lines +1087 to +1097
Copy link
Member

Choose a reason for hiding this comment

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

It's a bit unfortunate to have to search what we just wrote. Have to tried to check if fpconv_dtoa could be modified to always add the .0?

}
fbuffer_append_str(buffer, rb_funcall(obj, i_to_s, 0));
buffer->len += len;
}

static void generate_json_fragment(FBuffer *buffer, struct generate_json_data *data, JSON_Generator_State *state, VALUE obj)
Expand Down
3 changes: 3 additions & 0 deletions ext/json/ext/vendor/fpconv/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
The contents of this directory is extracted from https://github.com/night-shift/fpconv

It is licensed under the provisions of the Boost Software License - Version 1.0 - August 17th, 2003. See the ./license file for details.
Comment on lines +1 to +3
Copy link
Member

Choose a reason for hiding this comment

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

Maybe it's simpler to just add the license as comments at the top of fpconv.c?

23 changes: 23 additions & 0 deletions ext/json/ext/vendor/fpconv/license
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
Boost Software License - Version 1.0 - August 17th, 2003

Permission is hereby granted, free of charge, to any person or organization
obtaining a copy of the software and accompanying documentation covered by
this license (the "Software") to use, reproduce, display, distribute,
execute, and transmit the Software, and to prepare derivative works of the
Software, and to permit third-parties to whom the Software is furnished to
do so, all subject to the following:

The copyright notices in the Software and this entire statement, including
the above license grant, this restriction and the following disclaimer,
must be included in all copies of the Software, in whole or in part, and
all derivative works of the Software, unless such copies or derivative
works are solely in the form of machine-executable object code generated by
a source language processor.

THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE, TITLE AND NON-INFRINGEMENT. IN NO EVENT
SHALL THE COPYRIGHT HOLDERS OR ANYONE DISTRIBUTING THE SOFTWARE BE LIABLE
FOR ANY DAMAGES OR OTHER LIABILITY, WHETHER IN CONTRACT, TORT OR OTHERWISE,
ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
DEALINGS IN THE SOFTWARE.
Loading
Loading