Skip to content

Commit

Permalink
fix: ftoa
Browse files Browse the repository at this point in the history
  • Loading branch information
liuq19 committed Sep 12, 2024
1 parent a45fdaa commit a311680
Show file tree
Hide file tree
Showing 6 changed files with 152 additions and 81 deletions.
15 changes: 11 additions & 4 deletions include/sonic/dom/dynamicnode.h
Original file line number Diff line number Diff line change
Expand Up @@ -269,7 +269,7 @@ class DNode : public GenericNode<DNode<Allocator>> {

if (path[index].is_wildcard()) {
if (!this->IsObject() && !this->IsArray()) {
return true;
return false;
}
DNode* n = (DNode*)getChildrenFirstUnsafe() + (this->IsObject() ? 1 : 0);
size_t step = this->IsObject() ? 2 : 1;
Expand All @@ -296,11 +296,18 @@ class DNode : public GenericNode<DNode<Allocator>> {
if (!this->IsArray()) {
return false;
}
size_t idx = path[index].index();
if (idx >= this->Size()) {

// index maybe negative
int64_t idx = path[index].index();
if (idx < 0) {
idx = this->Size() + idx;
}

if (idx >= int64_t(this->Size()) || idx < 0) {
return false;
}
return this->findValueImpl(idx).atJsonPathImpl(path, index + 1, res);
return this->findValueImpl(size_t(idx))
.atJsonPathImpl(path, index + 1, res);
}
return false;
};
Expand Down
13 changes: 11 additions & 2 deletions include/sonic/dom/generic_document.h
Original file line number Diff line number Diff line change
Expand Up @@ -323,9 +323,18 @@ sonic_force_inline std::tuple<std::string, SonicError> GetByJsonPath(
return std::make_tuple("", result.error);
}

// check json path is wildcard
// filter the null nodes
result.nodes.erase(
std::remove_if(result.nodes.begin(), result.nodes.end(),
[](const auto& node) { return node->IsNull(); }),
result.nodes.end());

if (result.nodes.empty()) {
return std::make_tuple("null", kErrorNone);
}

WriteBuffer wb;
if (result.is_single) {
if (result.nodes.size() == 1) {
// not serialize the single string
auto& root = result.nodes[0];
if (root->IsString()) {
Expand Down
101 changes: 58 additions & 43 deletions include/sonic/dom/jsonpath.h
Original file line number Diff line number Diff line change
Expand Up @@ -18,43 +18,47 @@ namespace internal {
const char NONE = '\0';
const char WILDCARD = '*';
const char ROOT = '$';
const char IS_KEY = '\x01';
const char IS_INDEX = '\x02';

class JsonPathNode {
public:
JsonPathNode() noexcept = default;
JsonPathNode(int index) noexcept : index_(index) {}
JsonPathNode(StringView key) noexcept : key_(key) {}
JsonPathNode(int64_t index) noexcept : index_(index), token_(IS_INDEX) {}
JsonPathNode(StringView key) noexcept : key_(key), token_(IS_KEY) {}
JsonPathNode(char token) noexcept : token_(token) {}
~JsonPathNode() = default;

public:
bool is_wildcard() const noexcept { return token_ == '*'; }
bool is_wildcard() const noexcept { return token_ == WILDCARD; }

bool is_key() const noexcept { return index_ == -1 && token_ == '\0'; }
bool is_key() const noexcept { return token_ == IS_KEY; }

bool is_index() const noexcept { return index_ != -1; }
bool is_index() const noexcept { return token_ == IS_INDEX; }

bool is_root() const noexcept { return token_ == '$'; }
bool is_none() const noexcept { return token_ == NONE; }

bool is_root() const noexcept { return token_ == ROOT; }

StringView key() const noexcept {
sonic_assert(index_ == -1 && token_ == '\0');
sonic_assert(is_key());
return key_;
}

int index() const noexcept {
sonic_assert(index_ != -1 && token_ == '\0');
int64_t index() const noexcept {
sonic_assert(is_index());
return index_;
}

char token() const noexcept {
sonic_assert(index_ == -1 && key_ == "");
sonic_assert(!is_key() && !is_index() && !is_none());
return token_;
}

private:
int index_ = -1;
int64_t index_ = 0;
StringView key_ = "";
// special tokens
// record special tokens, also distinguish key and index
char token_ = '\0';
};

Expand Down Expand Up @@ -88,15 +92,46 @@ class JsonPath : public std::vector<JsonPathNode> {
return true;
}

// case as .123
sonic_force_inline bool parseRawIndex(StringView path, size_t& index,
JsonPathNode& node) {
int sum = 0;
// case as [123] or [-123]
sonic_force_inline bool parseBracktedIndex(StringView path, size_t& index,
JsonPathNode& node) {
uint64_t sum = 0;
int sign = 1;

// check negative
if (index < path.size() && path[index] == '-') {
index++;
sign = -1;
}

// check leading zero
if (index < path.size() && path[index] == '0') {
index++;
goto end;
}

while (index < path.size() && path[index] >= '0' && path[index] <= '9') {
sum = sum * 10 + (path[index] - '0');
auto last = sum * 10 + (path[index] - '0');
// check overflow
if (last < sum) {
return false;
}
sum = last;
index++;
}
node = JsonPathNode(sum);

// check overflow
if (sum > INT64_MAX) {
return false;
}

end:
// match ']'
if (index >= path.size() || path[index] != ']') {
return false;
}
index++;
node = JsonPathNode(int64_t(sum) * sign);
return true;
}

Expand Down Expand Up @@ -146,8 +181,9 @@ class JsonPath : public std::vector<JsonPathNode> {
}

// case as [abc]
sonic_force_inline bool parseBrackedUnquotedKey(StringView path, size_t& index,
JsonPathNode& node) {
sonic_force_inline bool parseBrackedUnquotedKey(StringView path,
size_t& index,
JsonPathNode& node) {
size_t start = index;
while (index < path.size() && path[index] != ']') {
index++;
Expand All @@ -160,18 +196,6 @@ class JsonPath : public std::vector<JsonPathNode> {
return true;
}

// case as [123]
sonic_force_inline bool parseBracktedIndex(StringView path, size_t& index,
JsonPathNode& node) {
if (!parseRawIndex(path, index, node)) {
return false;
}
if (path[index++] != ']') {
return false;
}
return true;
}

sonic_force_inline bool parseWildcard(StringView path, size_t& index,
JsonPathNode& node) {
if (index + 1 < path.size() && path[index] == '*' &&
Expand Down Expand Up @@ -211,12 +235,7 @@ class JsonPath : public std::vector<JsonPathNode> {
i++;
continue;
}

if (path[i] >= '0' && path[i] <= '9') {
valid = parseRawIndex(path, i, node);
} else {
valid = parseUnquotedKey(path, i, node);
}
valid = parseUnquotedKey(path, i, node);
} else if (path[i] == '[') {
if (i + 1 >= path.size()) {
return false;
Expand All @@ -235,13 +254,9 @@ class JsonPath : public std::vector<JsonPathNode> {

if (path[i] == '\'' || path[i] == '"') {
valid = parseQuotedName(path, i, node);
} else if (path[i] >= '0' && path[i] <= '9') {
} else if ((path[i] >= '0' && path[i] <= '9') || path[i] == '-') {
valid = parseBracktedIndex(path, i, node);
} else {
valid = false;
}
} else {
valid = false;
}

if (!valid) {
Expand Down
17 changes: 17 additions & 0 deletions include/sonic/internal/ftoa.h
Original file line number Diff line number Diff line change
Expand Up @@ -1035,9 +1035,22 @@ sonic_static_noinline int F64toa(char* out, double fp) {
/* fast path for integer */
if (q <= 0 && q >= -F64_SIG_BITS && IsDivPow2(c, -q)) {
uint64_t u = c >> -q;
#if SONIC_UES_EXPONENT
uint64_t exp = 0;
while ((u % 10) == 0) {
u /= 10;
exp += 1;
}
#endif
p = U64toa(p, u);
*p++ = '.';
*p++ = '0';
#if SONIC_UES_EXPONENT
if (exp != 0) {
*p++ = 'E';
p = U64toa(p, exp);
}
#endif
return p - out;
}

Expand All @@ -1050,7 +1063,11 @@ sonic_static_noinline int F64toa(char* out, double fp) {
int cnt = Ctz10(dec.sig);
int dot = cnt + dec.exp;
int sci_exp = dot - 1;
#if SONIC_UES_EXPONENT
bool exp_fmt = sci_exp != 0;
#else
bool exp_fmt = sci_exp < -6 || sci_exp > 20;
#endif
bool has_dot = dot < cnt;

if (exp_fmt) {
Expand Down
6 changes: 6 additions & 0 deletions include/sonic/macro.h
Original file line number Diff line number Diff line change
Expand Up @@ -80,6 +80,7 @@
#define SONIC_EXPONENT_ALWAYS_DOT 1
#define SONIC_EXPONENT_UPPERCASE 1
#define SONIC_EXPONENT_ALWAYS_SIGN 0
#define SONIC_UES_EXPONENT 1
#endif

#ifdef SONIC_DEFAULT_FORMAT
Expand All @@ -103,3 +104,8 @@
// print exponent with sign, including '+', like 1.0E+2
#define SONIC_EXPONENT_ALWAYS_SIGN 1
#endif

#ifndef SONIC_UES_EXPONENT
// use exponent format for float point number
#define SONIC_UES_EXPONENT 0
#endif
Loading

0 comments on commit a311680

Please sign in to comment.