From 01f2137d0ae2bac43c36b7c08c12e9e10c8631cb Mon Sep 17 00:00:00 2001 From: Francesco Chemolli Date: Fri, 30 May 2014 11:42:45 +0200 Subject: [PATCH] Fixed Parser::Tokenizer::int64, added more test cases --- src/parser/Tokenizer.cc | 70 ++++++++++++++++++++++++++++++++----- src/parser/testTokenizer.cc | 12 +++++++ 2 files changed, 73 insertions(+), 9 deletions(-) diff --git a/src/parser/Tokenizer.cc b/src/parser/Tokenizer.cc index 3c009c14b9..453f36a248 100644 --- a/src/parser/Tokenizer.cc +++ b/src/parser/Tokenizer.cc @@ -56,25 +56,77 @@ Parser::Tokenizer::skip(const char tokenChar) return false; } +/* reworked from compat/strtoll.c */ bool Parser::Tokenizer::int64(int64_t & result, int base) { if (buf_.isEmpty()) return false; - // API mismatch with strtoll: we don't eat leading space. - if (xisspace(buf_[0])) - return false; + //fixme: account for buf_.size() + bool neg = false; + const char *s = buf_.rawContent(); + const char *end = buf_.rawContent() + buf_.length(); + + if (*s == '-') { + neg = true; + ++s; + } else if (*s == '+') { + ++s; + } + if (s >= end) return false; + if (( base == 0 || base == 16) && *s == '0' && (s+1 <= end ) && + tolower(*(s+1)) == 'x') { + s += 2; + base = 16; + } + if (base == 0) { + if ( *s == '0') { + base = 8; + ++s; + } else { + base = 10; + } + } + if (s >= end) return false; + + uint64_t cutoff; - char *eon; - errno = 0; // reset errno + cutoff = neg ? -static_cast(INT64_MIN) : INT64_MAX; + int cutlim = cutoff % static_cast(base); + cutoff /= static_cast(base); - int64_t rv = strtoll(buf_.rawContent(), &eon, base); + int any = 0, c; + int64_t acc = 0; + for (c = *s++; s <= end; c = *s++) { + if (xisdigit(c)) { + c -= '0'; + } else if (xisalpha(c)) { + c -= xisupper(c) ? 'A' - 10 : 'a' - 10; + } else { + break; + } + if (c >= base) + break; + if (any < 0 || static_cast(acc) > cutoff || (static_cast(acc) == cutoff && c > cutlim)) + any = -1; + else { + any = 1; + acc *= base; + acc += c; + } + } - if (errno != 0) + if (any == 0) // nothing was parsed + return false; + if (any < 0) { + acc = neg ? INT64_MIN : INT64_MAX; + errno = ERANGE; return false; + } else if (neg) + acc = -acc; - buf_.consume(eon - buf_.rawContent()); // consume the parsed chunk - result = rv; + result = acc; + buf_.consume(s - buf_.rawContent() -1); return true; } diff --git a/src/parser/testTokenizer.cc b/src/parser/testTokenizer.cc index 1ea5200d75..4e4dfa5ae6 100644 --- a/src/parser/testTokenizer.cc +++ b/src/parser/testTokenizer.cc @@ -203,4 +203,16 @@ testTokenizer::testTokenizerInt64() CPPUNIT_ASSERT(t.int64(rv)); CPPUNIT_ASSERT_EQUAL(benchmark,rv); } + + // base-16, prefix + { + int64_t rv; + SBuf base("deadbeefrow"); + const int64_t benchmark=0xdeadbeef; + Parser::Tokenizer t(base); + CPPUNIT_ASSERT(t.int64(rv,16)); + CPPUNIT_ASSERT_EQUAL(benchmark,rv); + CPPUNIT_ASSERT_EQUAL(SBuf("row"),t.buf()); + + } } -- 2.39.5