diff --git a/ion/include/ion.h b/ion/include/ion.h index 74e62514b..8049b52f8 100644 --- a/ion/include/ion.h +++ b/ion/include/ion.h @@ -12,6 +12,8 @@ #include #include #include +#include +#include #include #include diff --git a/ion/include/ion/unicode/utf8_helper.h b/ion/include/ion/unicode/utf8_helper.h index 5dc1c6099..ca7123e0f 100644 --- a/ion/include/ion/unicode/utf8_helper.h +++ b/ion/include/ion/unicode/utf8_helper.h @@ -17,6 +17,9 @@ const char * CodePointSearch(const char * s, CodePoint c); * that should be lower if code points where removed before it. */ void CopyAndRemoveCodePoint(char * dst, size_t dstSize, const char * src, CodePoint c, const char * * indexToDUpdate = nullptr); +/* Copy src into dst until end of dst or code point c, with null termination. Return the length of the copy */ +size_t CopyUntilCodePoint(char * dst, size_t dstSize, const char * src, CodePoint c); + /* Perform actionCodePoint each time a given code point is found, and * actionOtherCodePoint for other code points. * goingRight tells if we are decoding towards the right or the left. If diff --git a/ion/src/shared/storage.cpp b/ion/src/shared/storage.cpp index b08ccc900..e2b61ed72 100644 --- a/ion/src/shared/storage.cpp +++ b/ion/src/shared/storage.cpp @@ -34,8 +34,8 @@ Storage::Record::Record(const char * fullName) { m_fullNameCRC32 = 0; return; } - const char * dotChar = strchr(fullName, k_dotChar); - assert(dotChar != nullptr); + const char * dotChar = UTF8Helper::CodePointSearch(fullName, k_dotChar); + assert(*dotChar != 0); assert(*(dotChar+1) != 0); // Assert there is an extension new (this) Record(fullName, dotChar - fullName, dotChar+1, (fullName + strlen(fullName)) - (dotChar+1)); } @@ -206,7 +206,7 @@ Storage::Record Storage::recordBaseNamedWithExtensions(const char * baseName, co if (strncmp(baseName, currentName, nameLength) == 0) { for (size_t i = 0; i < numberOfExtensions; i++) { if (strcmp(currentName+nameLength+1 /*+1 to pass the dot*/, extensions[i]) == 0) { - assert(*(currentName + nameLength) == '.'); + assert(UTF8Helper::CodePointIs(currentName + nameLength, '.')); return Record(currentName); } } @@ -375,6 +375,7 @@ size_t Storage::overrideFullNameAtPosition(char * position, const char * fullNam size_t Storage::overrideBaseNameWithExtensionAtPosition(char * position, const char * baseName, const char * extension) { size_t result = strlcpy(position, baseName, strlen(baseName)+1); // strlcpy copies the null terminating char + assert(UTF8Decoder::CharSizeOfCodePoint(k_dotChar) == 1); *(position+result) = k_dotChar; // Replace the null terminating char with a dot result++; result += strlcpy(position+result, extension, strlen(extension)+1); @@ -417,11 +418,11 @@ bool Storage::isNameOfRecordTaken(Record r, const Record * recordToExclude) { bool Storage::FullNameCompliant(const char * fullName) { // We check that there is one dot and one dot only. - const char * dotChar = strchr(fullName, k_dotChar); - if (dotChar == nullptr) { + const char * dotChar = UTF8Helper::CodePointSearch(fullName, k_dotChar); + if (*dotChar == 0) { return false; } - if (strchr(dotChar+1, k_dotChar) == nullptr) { + if (UTF8Helper::CodePointSearch(dotChar+1, k_dotChar) == 0) { return true; } return false; @@ -434,7 +435,7 @@ bool Storage::FullNameHasExtension(const char * fullName, const char * extension size_t fullNameLength = strlen(fullName); if (fullNameLength > extensionLength) { const char * ext = fullName + fullNameLength - extensionLength; - if (*(ext-1) == k_dotChar && strcmp(ext, extension) == 0) { + if (UTF8Helper::PreviousCodePointIs(fullName, ext, k_dotChar) && strcmp(ext, extension) == 0) { return true; } } diff --git a/ion/src/shared/unicode/utf8_helper.cpp b/ion/src/shared/unicode/utf8_helper.cpp index 9bc689032..42390bbf9 100644 --- a/ion/src/shared/unicode/utf8_helper.cpp +++ b/ion/src/shared/unicode/utf8_helper.cpp @@ -6,6 +6,7 @@ namespace UTF8Helper { static inline int minInt(int x, int y) { return x < y ? x : y; } +static inline size_t minSizeT(size_t x, size_t y) { return x < y ? x : y; } int CountOccurrences(const char * s, CodePoint c) { int count = 0; @@ -78,6 +79,22 @@ void CopyAndRemoveCodePoint(char * dst, size_t dstSize, const char * src, CodePo } } +size_t CopyUntilCodePoint(char * dst, size_t dstSize, const char * src, CodePoint c) { + UTF8Decoder decoder(src); + const char * codePointPointer = decoder.stringPosition(); + CodePoint codePoint = decoder.nextCodePoint(); + while (codePoint != UCodePointNull && codePoint != c) { + codePointPointer = decoder.stringPosition(); + codePoint = decoder.nextCodePoint(); + } + size_t copySize = minSizeT(dstSize - 1, codePointPointer - src); + assert(UTF8Helper::CodePointIs(src + copySize, 0) || UTF8Helper::CodePointIs(src + copySize, c)); + memmove(dst, src, copySize); + assert(copySize < dstSize); + dst[copySize] = 0; + return copySize; +} + const char * PerformAtCodePoints(const char * s, CodePoint c, CodePointAction actionCodePoint, CodePointAction actionOtherCodePoint, void * contextPointer, int contextInt, CodePoint stoppingCodePoint, bool goingRight, const char * initialPosition) { /* If we are decoding towards the left, we must have a starting position. If * we are decoding towards the right, the starting position is the start of diff --git a/poincare/include/poincare/left_parenthesis_layout.h b/poincare/include/poincare/left_parenthesis_layout.h index ffc0fe6df..8aaa41bde 100644 --- a/poincare/include/poincare/left_parenthesis_layout.h +++ b/poincare/include/poincare/left_parenthesis_layout.h @@ -19,7 +19,7 @@ public: // Serializable Node int serialize(char * buffer, int bufferSize, Preferences::PrintFloatMode floatDisplayMode, int numberOfSignificantDigits) const override { - return SerializationHelper::Char(buffer, bufferSize, '('); + return SerializationHelper::CodePoint(buffer, bufferSize, '('); } // TreeNode diff --git a/poincare/include/poincare/left_square_bracket_layout.h b/poincare/include/poincare/left_square_bracket_layout.h index 00720398a..c4f069c44 100644 --- a/poincare/include/poincare/left_square_bracket_layout.h +++ b/poincare/include/poincare/left_square_bracket_layout.h @@ -11,7 +11,7 @@ class LeftSquareBracketLayoutNode final : public SquareBracketLayoutNode { public: using SquareBracketLayoutNode::SquareBracketLayoutNode; int serialize(char * buffer, int bufferSize, Preferences::PrintFloatMode floatDisplayMode, int numberOfSignificantDigits) const override { - return SerializationHelper::Char(buffer, bufferSize, '['); + return SerializationHelper::CodePoint(buffer, bufferSize, '['); } bool isLeftBracket() const override { return true; } diff --git a/poincare/include/poincare/right_parenthesis_layout.h b/poincare/include/poincare/right_parenthesis_layout.h index 2f3f03d9f..4782c5fe3 100644 --- a/poincare/include/poincare/right_parenthesis_layout.h +++ b/poincare/include/poincare/right_parenthesis_layout.h @@ -19,7 +19,7 @@ public: // SerializableNode int serialize(char * buffer, int bufferSize, Preferences::PrintFloatMode floatDisplayMode, int numberOfSignificantDigits) const override { - return SerializationHelper::Char(buffer, bufferSize, ')'); + return SerializationHelper::CodePoint(buffer, bufferSize, ')'); } // TreeNode diff --git a/poincare/include/poincare/right_square_bracket_layout.h b/poincare/include/poincare/right_square_bracket_layout.h index 161ade869..e37132b85 100644 --- a/poincare/include/poincare/right_square_bracket_layout.h +++ b/poincare/include/poincare/right_square_bracket_layout.h @@ -11,7 +11,7 @@ class RightSquareBracketLayoutNode final : public SquareBracketLayoutNode { public: using SquareBracketLayoutNode::SquareBracketLayoutNode; int serialize(char * buffer, int bufferSize, Preferences::PrintFloatMode floatDisplayMode, int numberOfSignificantDigits) const override { - return SerializationHelper::Char(buffer, bufferSize, ']'); + return SerializationHelper::CodePoint(buffer, bufferSize, ']'); } bool isRightBracket() const override { return true; } diff --git a/poincare/include/poincare/sequence_layout.h b/poincare/include/poincare/sequence_layout.h index 28d2a48d5..9f5608adc 100644 --- a/poincare/include/poincare/sequence_layout.h +++ b/poincare/include/poincare/sequence_layout.h @@ -29,7 +29,7 @@ protected: constexpr static KDCoordinate k_boundHeightMargin = 2; constexpr static KDCoordinate k_argumentWidthMargin = 2; constexpr static const KDFont * k_font = KDFont::LargeFont; - constexpr static char k_equal[] = {'=', 0}; + constexpr static const char * k_equal = "="; KDSize lowerBoundSizeWithVariableEquals(); diff --git a/poincare/include/poincare/serialization_helper.h b/poincare/include/poincare/serialization_helper.h index 10126cce7..84ff6d32c 100644 --- a/poincare/include/poincare/serialization_helper.h +++ b/poincare/include/poincare/serialization_helper.h @@ -33,9 +33,7 @@ namespace SerializationHelper { const char * operatorName, bool writeFirstChild = true); - // Write one char in a buffer - int Char(char * buffer, int bufferSize, char c); - // Write one code point in a buffer + // Write one code point in a buffer and a null-terminating char int CodePoint(char * buffer, int bufferSize, CodePoint c); }; diff --git a/poincare/include/poincare/symbol.h b/poincare/include/poincare/symbol.h index e7715e65e..f608cb053 100644 --- a/poincare/include/poincare/symbol.h +++ b/poincare/include/poincare/symbol.h @@ -48,6 +48,7 @@ private: size_t nodeSize() const override { return sizeof(SymbolNode); } template Evaluation templatedApproximate(Context& context, Preferences::ComplexFormat complexFormat, Preferences::AngleUnit angleUnit) const; + bool isUnknownX() const; }; class Symbol final : public SymbolAbstract { @@ -57,7 +58,7 @@ class Symbol final : public SymbolAbstract { public: static constexpr int k_ansLength = 3; static constexpr char k_ans[k_ansLength+1] = "ans"; - static constexpr char k_unknownXReadableChar = 'x'; + static constexpr CodePoint k_unknownXReadableChar = 'x'; enum SpecialSymbols : char { /* We can use characters from 1 to 31 as they do not correspond to usual * characters but events as 'end of text', 'backspace'... */ diff --git a/poincare/src/bracket_pair_layout.cpp b/poincare/src/bracket_pair_layout.cpp index 345a9cf89..a7d751e1a 100644 --- a/poincare/src/bracket_pair_layout.cpp +++ b/poincare/src/bracket_pair_layout.cpp @@ -1,6 +1,7 @@ #include #include #include +#include extern "C" { #include #include @@ -96,7 +97,7 @@ int BracketPairLayoutNode::serialize(char * buffer, int bufferSize, Preferences: // Write the opening bracket int numberOfChar = 0; - buffer[numberOfChar++] = '['; + numberOfChar += SerializationHelper::CodePoint(buffer + numberOfChar, bufferSize - numberOfChar, '['); if (numberOfChar >= bufferSize-1) { return bufferSize-1;} // Write the argument @@ -104,7 +105,8 @@ int BracketPairLayoutNode::serialize(char * buffer, int bufferSize, Preferences: if (numberOfChar >= bufferSize-1) { return bufferSize-1; } // Write the closing bracket - buffer[numberOfChar++] = ']'; + numberOfChar += SerializationHelper::CodePoint(buffer + numberOfChar, bufferSize - numberOfChar, ']'); + if (numberOfChar >= bufferSize-1) { return bufferSize-1; } buffer[numberOfChar] = 0; return numberOfChar; } diff --git a/poincare/src/decimal.cpp b/poincare/src/decimal.cpp index ba192e739..62dcab336 100644 --- a/poincare/src/decimal.cpp +++ b/poincare/src/decimal.cpp @@ -6,8 +6,9 @@ #include #include #include +#include +#include #include -#include #include #include @@ -111,8 +112,7 @@ int DecimalNode::convertToText(char * buffer, int bufferSize, Preferences::Print int currentChar = 0; if (currentChar >= bufferSize-1) { return bufferSize-1; } if (unsignedMantissa().isZero()) { - buffer[currentChar++] = '0'; - buffer[currentChar] = 0; + currentChar += SerializationHelper::CodePoint(buffer + currentChar, bufferSize - currentChar, '0'); // This already writes the null terminating char return currentChar; } int exponent = m_exponent; @@ -136,14 +136,14 @@ int DecimalNode::convertToText(char * buffer, int bufferSize, Preferences::Print removeZeroAtTheEnd(&m); } if (m_negative) { - buffer[currentChar++] = '-'; + currentChar += SerializationHelper::CodePoint(buffer + currentChar, bufferSize - currentChar, '-'); if (currentChar >= bufferSize-1) { return bufferSize-1; } } int mantissaLength = m.serialize(tempBuffer, PrintFloat::k_numberOfStoredSignificantDigits+1); // Assert that m is not +/-inf assert(strcmp(tempBuffer, Infinity::Name()) != 0); - assert(!(tempBuffer[0] == '-' && strcmp(&tempBuffer[1], Infinity::Name()) == 0)); + assert(!(UTF8Helper::CodePointIs(tempBuffer, '-') && strcmp(&tempBuffer[1], Infinity::Name()) == 0)); if (strcmp(tempBuffer, Undefined::Name()) == 0) { currentChar += strlcpy(buffer+currentChar, tempBuffer, bufferSize-currentChar); @@ -168,10 +168,20 @@ int DecimalNode::convertToText(char * buffer, int bufferSize, Preferences::Print if (mantissaLength == 1) { currentChar += strlcpy(buffer+currentChar, tempBuffer, bufferSize-currentChar); } else { + /* Forward one char: _ + * Write the mantissa _23456 + * Copy the most significant digit on the forwarded char: 223456 + * Write the dot : 2.3456 + * + * We should use the UTF8Helper to manipulate chars, but it is clearer to + * manipulate chars directly, so we just put assumptions on the char size + * of the code points we manipuate. */ + assert(UTF8Decoder::CharSizeOfCodePoint('.') == 1); currentChar++; - int decimalMarkerPosition = currentChar; if (currentChar >= bufferSize-1) { return bufferSize-1; } + int decimalMarkerPosition = currentChar; currentChar += strlcpy(buffer+currentChar, tempBuffer, bufferSize-currentChar); + assert(UTF8Decoder::CharSizeOfCodePoint(buffer[decimalMarkerPosition]) == 1); buffer[decimalMarkerPosition-1] = buffer[decimalMarkerPosition]; buffer[decimalMarkerPosition] = '.'; } @@ -189,12 +199,8 @@ int DecimalNode::convertToText(char * buffer, int bufferSize, Preferences::Print strlcpy(buffer+currentChar+deltaCharMantissa, tempBuffer, maxInt(0, bufferSize-deltaCharMantissa-currentChar)); if (exponent < 0) { for (int i = 0; i <= -exponent; i++) { + currentChar += SerializationHelper::CodePoint(buffer + currentChar, bufferSize - currentChar, i == 1 ? '.' : '0'); if (currentChar >= bufferSize-1) { return bufferSize-1; } - if (i == 1) { - buffer[currentChar++] = '.'; - continue; - } - buffer[currentChar++] = '0'; } } currentChar += mantissaLength; @@ -205,17 +211,18 @@ int DecimalNode::convertToText(char * buffer, int bufferSize, Preferences::Print buffer[i+1] = buffer[i]; } if (currentChar >= bufferSize-1) { return bufferSize-1; } + assert(UTF8Decoder::CharSizeOfCodePoint('.') == 1); buffer[decimalMarkerPosition+1] = '.'; currentChar++; } + if (currentChar+1 >= bufferSize-1) { return bufferSize-1; } if (exponent >= 0 && exponent > mantissaLength-1) { int endMarkerPosition = m_negative ? exponent+1 : exponent; for (int i = currentChar-1; i < endMarkerPosition; i++) { + currentChar += SerializationHelper::CodePoint(buffer + currentChar, bufferSize - currentChar, '0'); if (currentChar+1 >= bufferSize-1) { return bufferSize-1; } - buffer[currentChar++] = '0'; } } - if (currentChar >= bufferSize-1) { return bufferSize-1; } buffer[currentChar] = 0; return currentChar; } @@ -228,7 +235,7 @@ template T DecimalNode::templatedApproximate() const { } int Decimal::Exponent(const char * integralPart, int integralPartLength, const char * fractionalPart, int fractionalPartLength, const char * exponent, int exponentLength, bool exponentNegative) { - if (exponentLength > 0 && exponent[0] == '-') { + if (exponentLength > 0 && UTF8Helper::CodePointIs(exponent, '-')) { exponent++; exponentNegative = true; exponentLength--; @@ -237,6 +244,7 @@ int Decimal::Exponent(const char * integralPart, int integralPartLength, const c int exp = 0; for (int i = 0; i < exponentLength; i++) { exp *= base; + assert(*exponent >= '0' && *exponent <= '9'); exp += *exponent-'0'; exponent++; } @@ -253,7 +261,7 @@ int Decimal::Exponent(const char * integralPart, int integralPartLength, const c if (integralPart == integralPartEnd) { const char * fractionalPartEnd = fractionalPart + fractionalPartLength; if (fractionalPart != nullptr) { - while (*fractionalPart == '0' && fractionalPart < fractionalPartEnd) { + while (UTF8Helper::CodePointIs(fractionalPart, '0') && fractionalPart < fractionalPartEnd) { fractionalPart++; exp--; } @@ -271,7 +279,7 @@ Decimal Decimal::Builder(const char * integralPart, int integralPartLength, cons Integer zero(0); Integer base(10); // Get rid of useless preceeding 0s - while (*integralPart == '0' && integralPartLength > 1) { + while (UTF8Helper::CodePointIs(integralPart, '0') && integralPartLength > 1) { integralPart++; integralPartLength--; } @@ -282,9 +290,9 @@ Decimal Decimal::Builder(const char * integralPart, int integralPartLength, cons Integer numerator(integralPart, integralPartLength, false); assert(!numerator.isOverflow()); // Special case for 0.??? : get rid of useless 0s in front of the integralPartLength - if (fractionalPart != nullptr && integralPartLength == 1 && integralPart[0] == '0') { + if (fractionalPart != nullptr && integralPartLength == 1 && UTF8Helper::CodePointIs(integralPart, '0')) { integralPartLength = 0; - while (*fractionalPart == '0') { + while (UTF8Helper::CodePointIs(fractionalPart, '0')) { fractionalPart++; fractionalPartLength--; } @@ -293,6 +301,7 @@ Decimal Decimal::Builder(const char * integralPart, int integralPartLength, cons fractionalPartLength = integralPartLength+fractionalPartLength > PrintFloat::k_numberOfStoredSignificantDigits ? PrintFloat::k_numberOfStoredSignificantDigits - integralPartLength : fractionalPartLength; for (int i = 0; i < fractionalPartLength; i++) { numerator = Integer::Multiplication(numerator, base); + assert(*fractionalPart >= '0' && *fractionalPart <= '9'); numerator = Integer::Addition(numerator, Integer(*fractionalPart-'0')); fractionalPart++; } diff --git a/poincare/src/factorial.cpp b/poincare/src/factorial.cpp index 9c777a44a..ff04ca490 100644 --- a/poincare/src/factorial.cpp +++ b/poincare/src/factorial.cpp @@ -1,13 +1,13 @@ #include -#include #include +#include #include -#include -#include -#include -#include #include -#include +#include +#include +#include +#include +#include #include namespace Poincare { @@ -73,18 +73,18 @@ int FactorialNode::serialize(char * buffer, int bufferSize, Preferences::PrintFl buffer[bufferSize-1] = 0; int numberOfChar = 0; if (childNeedsParenthesis(childAtIndex(0))) { - buffer[numberOfChar++] = '('; + numberOfChar += SerializationHelper::CodePoint(&buffer[numberOfChar], bufferSize-numberOfChar, '('); if (numberOfChar >= bufferSize-1) { return bufferSize-1; } } numberOfChar += childAtIndex(0)->serialize(buffer+numberOfChar, bufferSize-numberOfChar, floatDisplayMode, numberOfSignificantDigits); if (childNeedsParenthesis(childAtIndex(0))) { - buffer[numberOfChar++] = ')'; + numberOfChar += SerializationHelper::CodePoint(&buffer[numberOfChar], bufferSize-numberOfChar, ')'); if (numberOfChar >= bufferSize-1) { return bufferSize-1; } } if (numberOfChar >= bufferSize-1) { return numberOfChar; } - buffer[numberOfChar++] = '!'; + numberOfChar += SerializationHelper::CodePoint(&buffer[numberOfChar], bufferSize-numberOfChar, '!'); buffer[numberOfChar] = 0; return numberOfChar; } diff --git a/poincare/src/fraction_layout.cpp b/poincare/src/fraction_layout.cpp index 9128e9ccb..30d4b2a78 100644 --- a/poincare/src/fraction_layout.cpp +++ b/poincare/src/fraction_layout.cpp @@ -142,7 +142,7 @@ int FractionLayoutNode::serialize(char * buffer, int bufferSize, Preferences::Pr if (idxInParent >= 0 && idxInParent < (p->numberOfChildren() - 1) && p->isHorizontal() && p->childAtIndex(idxInParent + 1)->isVerticalOffset()) { addParenthesis = true; // Add parenthesis - buffer[numberOfChar++] = '('; + numberOfChar+= SerializationHelper::CodePoint(buffer + numberOfChar, bufferSize - numberOfChar, '('); if (numberOfChar >= bufferSize-1) { return bufferSize-1;} } @@ -152,7 +152,7 @@ int FractionLayoutNode::serialize(char * buffer, int bufferSize, Preferences::Pr if (addParenthesis) { // Add parenthesis - buffer[numberOfChar++] = ')'; + numberOfChar+= SerializationHelper::CodePoint(buffer + numberOfChar, bufferSize - numberOfChar, ')'); if (numberOfChar >= bufferSize-1) { return bufferSize-1;} } diff --git a/poincare/src/integer.cpp b/poincare/src/integer.cpp index da7320d06..2c38ce69c 100644 --- a/poincare/src/integer.cpp +++ b/poincare/src/integer.cpp @@ -1,6 +1,8 @@ #include #include #include +#include +#include #include #include extern "C" { @@ -135,7 +137,7 @@ Integer::Integer(double_native_int_t i) { Integer::Integer(const char * digits, size_t length, bool negative) : Integer(0) { - if (digits != nullptr && digits[0] == '-') { + if (digits != nullptr && UTF8Helper::CodePointIs(digits, '-')) { negative = true; digits++; length--; @@ -144,7 +146,7 @@ Integer::Integer(const char * digits, size_t length, bool negative) : Integer base(10); for (size_t i = 0; i < length; i++) { *this = Multiplication(*this, base); - *this = Addition(*this, Integer(*digits-'0')); + *this = Addition(*this, Integer(*digits - '0')); digits++; } } @@ -158,6 +160,9 @@ int Integer::serialize(char * buffer, int bufferSize) const { return -1; } buffer[bufferSize-1] = 0; + if (bufferSize == 1) { + return 0; + } if (isOverflow()) { return PrintFloat::convertFloatToText(m_negative ? -INFINITY : INFINITY, buffer, bufferSize, PrintFloat::k_numberOfStoredSignificantDigits, Preferences::PrintFloatMode::Decimal); } @@ -166,14 +171,12 @@ int Integer::serialize(char * buffer, int bufferSize) const { Integer abs = *this; abs.setNegative(false); IntegerDivision d = udiv(abs, base); + int size = 0; - if (bufferSize == 1) { - return 0; - } if (isZero()) { - buffer[size++] = '0'; + size += SerializationHelper::CodePoint(buffer + size, bufferSize - size, '0'); } else if (isNegative()) { - buffer[size++] = '-'; + size += SerializationHelper::CodePoint(buffer + size, bufferSize - size, '-'); } while (!(d.remainder.isZero() && @@ -182,9 +185,10 @@ int Integer::serialize(char * buffer, int bufferSize) const { if (size >= bufferSize-1) { return PrintFloat::convertFloatToText(NAN, buffer, bufferSize, PrintFloat::k_numberOfStoredSignificantDigits, Preferences::PrintFloatMode::Decimal); } - buffer[size++] = c; + size += SerializationHelper::CodePoint(buffer + size, bufferSize - size, c); d = udiv(d.quotient, base); } + assert(size <= bufferSize - 1); buffer[size] = 0; // Flip the string diff --git a/poincare/src/integral_layout.cpp b/poincare/src/integral_layout.cpp index 9af734d8d..d6962dcc8 100644 --- a/poincare/src/integral_layout.cpp +++ b/poincare/src/integral_layout.cpp @@ -160,7 +160,7 @@ int IntegralLayoutNode::serialize(char * buffer, int bufferSize, Preferences::Pr } // Write the opening parenthesis - buffer[numberOfChar++] = '('; + numberOfChar += SerializationHelper::CodePoint(buffer + numberOfChar, bufferSize - numberOfChar, '('); if (numberOfChar >= bufferSize-1) { return bufferSize-1; } @@ -170,6 +170,7 @@ int IntegralLayoutNode::serialize(char * buffer, int bufferSize, Preferences::Pr if (i != 0) { // Write the comma buffer[numberOfChar++] = ','; + numberOfChar += SerializationHelper::CodePoint(buffer + numberOfChar, bufferSize - numberOfChar, '('); if (numberOfChar >= bufferSize-1) { return bufferSize-1; } } @@ -179,7 +180,7 @@ int IntegralLayoutNode::serialize(char * buffer, int bufferSize, Preferences::Pr } // Write the closing parenthesis - buffer[numberOfChar++] = ')'; + numberOfChar += SerializationHelper::CodePoint(buffer + numberOfChar, bufferSize - numberOfChar, ')'); buffer[numberOfChar] = 0; return numberOfChar; } diff --git a/poincare/src/matrix.cpp b/poincare/src/matrix.cpp index f2faeef90..9ec4b0953 100644 --- a/poincare/src/matrix.cpp +++ b/poincare/src/matrix.cpp @@ -1,9 +1,10 @@ #include -#include #include -#include #include #include +#include +#include +#include #include #include #include @@ -40,12 +41,12 @@ int MatrixNode::serialize(char * buffer, int bufferSize, Preferences::PrintFloat if (currentChar >= bufferSize-1) { return 0; } - buffer[currentChar++] = '['; + currentChar += SerializationHelper::CodePoint(buffer + currentChar, bufferSize - currentChar, '['); if (currentChar >= bufferSize-1) { return currentChar; } for (int i = 0; i < m_numberOfRows; i++) { - buffer[currentChar++] = '['; + currentChar += SerializationHelper::CodePoint(buffer + currentChar, bufferSize - currentChar, '['); if (currentChar >= bufferSize-1) { return currentChar; } @@ -54,7 +55,7 @@ int MatrixNode::serialize(char * buffer, int bufferSize, Preferences::PrintFloat return currentChar; } for (int j = 1; j < m_numberOfColumns; j++) { - buffer[currentChar++] = ','; + currentChar += SerializationHelper::CodePoint(buffer + currentChar, bufferSize - currentChar, ','); if (currentChar >= bufferSize-1) { return currentChar; } @@ -67,12 +68,15 @@ int MatrixNode::serialize(char * buffer, int bufferSize, Preferences::PrintFloat if (currentChar >= bufferSize-1) { return currentChar; } - buffer[currentChar++] = ']'; + currentChar += SerializationHelper::CodePoint(buffer + currentChar, bufferSize - currentChar, ']'); if (currentChar >= bufferSize-1) { return currentChar; } } - buffer[currentChar++] = ']'; + currentChar += SerializationHelper::CodePoint(buffer + currentChar, bufferSize - currentChar, ']'); + if (currentChar >= bufferSize-1) { + return currentChar; + } buffer[currentChar] = 0; return currentChar; } diff --git a/poincare/src/matrix_layout.cpp b/poincare/src/matrix_layout.cpp index e7223c3e7..a5d565c6a 100644 --- a/poincare/src/matrix_layout.cpp +++ b/poincare/src/matrix_layout.cpp @@ -101,22 +101,23 @@ int MatrixLayoutNode::serialize(char * buffer, int bufferSize, Preferences::Prin return 1; } int numberOfChar = 0; - buffer[numberOfChar++] = '['; + numberOfChar += SerializationHelper::CodePoint(buffer + numberOfChar, bufferSize - numberOfChar, '['); if (numberOfChar >= bufferSize-1) { return bufferSize-1;} int maxRowIndex = hasGreySquares() ? m_numberOfRows - 1 : m_numberOfRows; int maxColumnIndex = hasGreySquares() ? m_numberOfColumns - 2 : m_numberOfColumns - 1; for (int i = 0; i < maxRowIndex; i++) { - buffer[numberOfChar++] = '['; + numberOfChar += SerializationHelper::CodePoint(buffer + numberOfChar, bufferSize - numberOfChar, '['); if (numberOfChar >= bufferSize-1) { return bufferSize-1;} numberOfChar += SerializationHelper::Infix(this, buffer+numberOfChar, bufferSize-numberOfChar, floatDisplayMode, numberOfSignificantDigits, ",", i*m_numberOfColumns, i* m_numberOfColumns + maxColumnIndex); if (numberOfChar >= bufferSize-1) { return bufferSize-1; } - buffer[numberOfChar++] = ']'; + numberOfChar += SerializationHelper::CodePoint(buffer + numberOfChar, bufferSize - numberOfChar, ']'); if (numberOfChar >= bufferSize-1) { return bufferSize-1; } } - buffer[numberOfChar++] = ']'; + numberOfChar += SerializationHelper::CodePoint(buffer + numberOfChar, bufferSize - numberOfChar, ']'); + if (numberOfChar >= bufferSize-1) { return bufferSize-1; } buffer[numberOfChar] = 0; return numberOfChar; } diff --git a/poincare/src/nth_root_layout.cpp b/poincare/src/nth_root_layout.cpp index 875186161..c689064d0 100644 --- a/poincare/src/nth_root_layout.cpp +++ b/poincare/src/nth_root_layout.cpp @@ -173,7 +173,7 @@ int NthRootLayoutNode::serialize(char * buffer, int bufferSize, Preferences::Pri return bufferSize-1; } - buffer[numberOfChar++] = '('; + numberOfChar += SerializationHelper::CodePoint(buffer + numberOfChar, bufferSize - numberOfChar, '('); if (numberOfChar >= bufferSize-1) { return bufferSize-1; } @@ -181,7 +181,7 @@ int NthRootLayoutNode::serialize(char * buffer, int bufferSize, Preferences::Pri numberOfChar += (const_cast(this))->radicandLayout()->serialize(buffer+numberOfChar, bufferSize-numberOfChar, floatDisplayMode, numberOfSignificantDigits); if (numberOfChar >= bufferSize-1) { return bufferSize-1; } - buffer[numberOfChar++] = ')'; + numberOfChar += SerializationHelper::CodePoint(buffer + numberOfChar, bufferSize - numberOfChar, ')'); buffer[numberOfChar] = 0; return numberOfChar; } diff --git a/poincare/src/opposite.cpp b/poincare/src/opposite.cpp index 071f7b8c8..acc25f9fe 100644 --- a/poincare/src/opposite.cpp +++ b/poincare/src/opposite.cpp @@ -3,12 +3,13 @@ #include #include #include -#include #include #include #include +#include #include extern "C" { +#include #include #include } @@ -57,7 +58,10 @@ int OppositeNode::serialize(char * buffer, int bufferSize, Preferences::PrintFlo buffer[bufferSize-1] = 0; int numberOfChar = 0; if (bufferSize == 1) { return 0; } - buffer[numberOfChar++] = '-'; + numberOfChar += SerializationHelper::CodePoint(buffer + numberOfChar, bufferSize - numberOfChar, '-'); + if (numberOfChar >= bufferSize - 1) { + return bufferSize - 1; + } numberOfChar += childAtIndex(0)->serialize(buffer+numberOfChar, bufferSize-numberOfChar, floatDisplayMode, numberOfSignificantDigits); buffer[numberOfChar] = 0; return numberOfChar; diff --git a/poincare/src/parsing/tokenizer.cpp b/poincare/src/parsing/tokenizer.cpp index 26bd6d7bd..878e442ec 100644 --- a/poincare/src/parsing/tokenizer.cpp +++ b/poincare/src/parsing/tokenizer.cpp @@ -1,6 +1,7 @@ #include "tokenizer.h" #include #include +#include namespace Poincare { @@ -79,7 +80,7 @@ Token Tokenizer::popNumber() { const char * fractionalPartText = m_text; size_t fractionalPartLength = 0; - assert(integralPartLength > 0 || *m_text == '.'); + assert(integralPartLength > 0 || UTF8Helper::CodePointIs(m_text, '.')); if (canPopCodePoint('.')) { fractionalPartText = m_text; fractionalPartLength = popDigits(); diff --git a/poincare/src/print_float.cpp b/poincare/src/print_float.cpp index cce3d89e7..89a27c61b 100644 --- a/poincare/src/print_float.cpp +++ b/poincare/src/print_float.cpp @@ -5,6 +5,8 @@ #include #include #include +#include +#include extern "C" { #include #include @@ -24,19 +26,26 @@ void PrintFloat::printBase10IntegerWithDecimalMarker(char * buffer, int bufferLe constexpr int tempBufferSize = PrintFloat::k_maxFloatBufferLength; char tempBuffer[tempBufferSize]; int intLength = i.serialize(tempBuffer, tempBufferSize); - int firstDigitChar = tempBuffer[0] == '-' ? 1 : 0; + int firstDigitChar = UTF8Helper::CodePointIs(tempBuffer, '-') ? 1 : 0; + /* We should use the UTF8Decoder to write code points in buffers, but it is + * much clearer to manipulate chars directly as we know that the code point we + * use ('.', '0, '1', '2', ...) are only one char long. */ for (int k = bufferLength-1; k >= firstDigitChar; k--) { if (k == decimalMarkerPosition) { + assert(UTF8Decoder::CharSizeOfCodePoint('.') == 1); buffer[k] = '.'; continue; } if (intLength > firstDigitChar) { + assert(UTF8Decoder::CharSizeOfCodePoint(tempBuffer[intLength-1]) == 1); buffer[k] = tempBuffer[--intLength]; continue; } + assert(UTF8Decoder::CharSizeOfCodePoint('0') == 1); buffer[k] = '0'; } if (firstDigitChar == 1) { + assert(UTF8Decoder::CharSizeOfCodePoint(tempBuffer[0]) == 1); buffer[0] = tempBuffer[0]; } } @@ -82,7 +91,7 @@ int PrintFloat::convertFloatToTextPrivate(T f, char * buffer, int bufferSize, in assert(Infinity::NameSize()+1 < bufferSize); int currentChar = 0; if (f < 0) { - buffer[currentChar++] = '-'; + currentChar+= SerializationHelper::CodePoint(buffer + currentChar, bufferSize - currentChar, '-'); } strlcpy(&buffer[currentChar], Infinity::Name(), bufferSize-1); return currentChar + Infinity::NameSize() - 1; @@ -149,6 +158,7 @@ int PrintFloat::convertFloatToTextPrivate(T f, char * buffer, int bufferSize, in int numberOfZerosRemoved = 0; while (digit.isZero() && numberOfCharsForMantissaWithoutSign > minimumNumberOfCharsInMantissa && (numberOfCharsForMantissaWithoutSign > exponentInBase10+1 || mode == Preferences::PrintFloatMode::Scientific)) { + assert(UTF8Decoder::CharSizeOfCodePoint('0') == 1); numberOfCharsForMantissaWithoutSign--; dividend = quotient; quotient = Integer::Division(dividend, Integer(10)).quotient; @@ -164,12 +174,14 @@ int PrintFloat::convertFloatToTextPrivate(T f, char * buffer, int bufferSize, in // Force a decimal marker if there is fractional part bool decimalMarker = (mode == Preferences::PrintFloatMode::Scientific && numberOfCharsForMantissaWithoutSign > 1) || (mode == Preferences::PrintFloatMode::Decimal && numberOfCharsForMantissaWithoutSign > exponentInBase10 +1); if (decimalMarker) { + assert(UTF8Decoder::CharSizeOfCodePoint('.') == 1); numberOfCharsForMantissaWithoutSign++; } /* Find the position of the decimal marker position */ int decimalMarkerPosition = exponentInBase10 < 0 || mode == Preferences::PrintFloatMode::Scientific ? 1 : exponentInBase10+1; decimalMarkerPosition = f < 0 ? decimalMarkerPosition+1 : decimalMarkerPosition; + assert(UTF8Decoder::CharSizeOfCodePoint('-') == 1); /* Part III: Exponent */ @@ -177,10 +189,12 @@ int PrintFloat::convertFloatToTextPrivate(T f, char * buffer, int bufferSize, in if (exponentInBase10 < 0){ // If the exponent is < 0, we need a additional char for the sign numberOfCharExponent++; + assert(UTF8Decoder::CharSizeOfCodePoint('-') == 1); } /* Part III: print mantissa*10^exponent */ int numberOfCharsForMantissaWithSign = f >= 0 ? numberOfCharsForMantissaWithoutSign : numberOfCharsForMantissaWithoutSign + 1; + assert(UTF8Decoder::CharSizeOfCodePoint('-') == 1); // Print mantissa assert(!dividend.isOverflow()); if (numberOfCharsForMantissaWithSign >= bufferSize) { diff --git a/poincare/src/rational.cpp b/poincare/src/rational.cpp index 3379dec01..e5e9ee87a 100644 --- a/poincare/src/rational.cpp +++ b/poincare/src/rational.cpp @@ -1,16 +1,16 @@ #include +#include +#include +#include +#include +#include +#include extern "C" { #include #include #include #include } -#include -#include -#include -#include -#include - namespace Poincare { /* Rational Node */ @@ -78,7 +78,10 @@ int RationalNode::serialize(char * buffer, int bufferSize, Preferences::PrintFlo if (numberOfChar >= bufferSize-1) { return numberOfChar; } - buffer[numberOfChar++] = '/'; + numberOfChar += SerializationHelper::CodePoint(buffer + numberOfChar, bufferSize - numberOfChar, '/'); + if (numberOfChar >= bufferSize-1) { + return numberOfChar; + } numberOfChar += denominator().serialize(buffer+numberOfChar, bufferSize-numberOfChar); return numberOfChar; } diff --git a/poincare/src/sequence_layout.cpp b/poincare/src/sequence_layout.cpp index 7e98ecfa9..ba4ac205c 100644 --- a/poincare/src/sequence_layout.cpp +++ b/poincare/src/sequence_layout.cpp @@ -9,8 +9,6 @@ namespace Poincare { static inline KDCoordinate maxCoordinate(KDCoordinate x, KDCoordinate y) { return x > y ? x : y; } -constexpr char SequenceLayoutNode::k_equal[]; - void SequenceLayoutNode::moveCursorLeft(LayoutCursor * cursor, bool * shouldRecomputeLayout) { if (cursor->layoutNode() == upperBoundLayout()) { @@ -215,14 +213,14 @@ int SequenceLayoutNode::writeDerivedClassInBuffer(const char * operatorName, cha if (numberOfChar >= bufferSize-1) { return bufferSize-1; } // Write the opening parenthesis - buffer[numberOfChar++] = '('; + numberOfChar += SerializationHelper::CodePoint(buffer + numberOfChar, bufferSize - numberOfChar, '('); if (numberOfChar >= bufferSize-1) { return bufferSize-1; } LayoutNode * argLayouts[] = {const_cast(this)->argumentLayout(), const_cast(this)->variableLayout(), const_cast(this)->lowerBoundLayout(), const_cast(this)->upperBoundLayout()}; for (uint8_t i = 0; i < sizeof(argLayouts)/sizeof(argLayouts[0]); i++) { if (i != 0) { // Write the comma - buffer[numberOfChar++] = ','; + numberOfChar += SerializationHelper::CodePoint(buffer + numberOfChar, bufferSize - numberOfChar, ','); if (numberOfChar >= bufferSize-1) { return bufferSize-1; } } numberOfChar += argLayouts[i]->serialize(buffer+numberOfChar, bufferSize-numberOfChar, floatDisplayMode, numberOfSignificantDigits); @@ -230,7 +228,7 @@ int SequenceLayoutNode::writeDerivedClassInBuffer(const char * operatorName, cha } // Write the closing parenthesis - buffer[numberOfChar++] = ')'; + numberOfChar += SerializationHelper::CodePoint(buffer + numberOfChar, bufferSize - numberOfChar, ')'); buffer[numberOfChar] = 0; return numberOfChar; } diff --git a/poincare/src/serialization_helper.cpp b/poincare/src/serialization_helper.cpp index 0851bd782..770acec6b 100644 --- a/poincare/src/serialization_helper.cpp +++ b/poincare/src/serialization_helper.cpp @@ -5,8 +5,6 @@ namespace Poincare { -static inline int minInt(int x, int y) { return x < y ? x : y; } - static bool checkBufferSize(char * buffer, int bufferSize, int * result) { // If buffer has size 0 or 1, put a zero if it fits and return if (bufferSize == 0) { @@ -41,7 +39,7 @@ static int serializeChild( // Write the child with parentheses if needed bool addParentheses = parentNode->childNeedsParenthesis(childNode); if (addParentheses) { - buffer[numberOfChar++] = '('; + numberOfChar += UTF8Decoder::CodePointToChars('(', buffer+numberOfChar, bufferSize - numberOfChar); if (numberOfChar >= bufferSize-1) { return bufferSize-1; } @@ -52,7 +50,11 @@ static int serializeChild( return bufferSize-1; } if (addParentheses) { - buffer[numberOfChar++] = ')'; + numberOfChar += UTF8Decoder::CodePointToChars(')', buffer+numberOfChar, bufferSize - numberOfChar); + } + if (numberOfChar >= bufferSize-1) { + assert(buffer[bufferSize - 1] == 0); + return bufferSize-1; } buffer[numberOfChar] = 0; return numberOfChar; @@ -132,7 +134,7 @@ int SerializationHelper::Prefix( } // Add the opening parenthese - buffer[numberOfChar++] = '('; + numberOfChar += UTF8Decoder::CodePointToChars('(', buffer+numberOfChar, bufferSize - numberOfChar); if (numberOfChar >= bufferSize-1) { return bufferSize-1; } @@ -153,7 +155,7 @@ int SerializationHelper::Prefix( // Write the remaining children, separated with commas for (int i = firstChildIndex + 1; i < childrenCount; i++) { - buffer[numberOfChar++] = ','; + numberOfChar += UTF8Decoder::CodePointToChars(',', buffer+numberOfChar, bufferSize - numberOfChar); if (numberOfChar >= bufferSize-1) { return bufferSize-1; } @@ -166,23 +168,14 @@ int SerializationHelper::Prefix( } // Add the closing parenthese - buffer[numberOfChar++] = ')'; + numberOfChar += UTF8Decoder::CodePointToChars(')', buffer+numberOfChar, bufferSize - numberOfChar); + if (numberOfChar >= bufferSize-1) { + return bufferSize-1; + } buffer[numberOfChar] = 0; return numberOfChar; } -int SerializationHelper::Char(char * buffer, int bufferSize, char c) { - { - int result = 0; - if (checkBufferSize(buffer, bufferSize, &result)) { - return result; - } - } - buffer[0] = c; - buffer[1] = 0; - return 1; -} - int SerializationHelper::CodePoint(char * buffer, int bufferSize, class CodePoint c) { { int result = 0; @@ -191,9 +184,9 @@ int SerializationHelper::CodePoint(char * buffer, int bufferSize, class CodePoin } } size_t size = UTF8Decoder::CodePointToChars(c, buffer, bufferSize); - int nullTerminatingIndex = minInt(size, bufferSize - 1); - buffer[nullTerminatingIndex] = 0; - return nullTerminatingIndex; + assert(size <= bufferSize - 1); + buffer[size] = 0; + return size; } } diff --git a/poincare/src/symbol.cpp b/poincare/src/symbol.cpp index de844615a..52ef984ea 100644 --- a/poincare/src/symbol.cpp +++ b/poincare/src/symbol.cpp @@ -7,13 +7,14 @@ #include #include #include -#include +#include #include #include namespace Poincare { constexpr char Symbol::k_ans[]; +constexpr CodePoint Symbol::k_unknownXReadableChar; SymbolNode::SymbolNode(const char * newName, int length) : SymbolAbstractNode() { strlcpy(const_cast(name()), newName, length+1); @@ -28,7 +29,7 @@ Expression SymbolNode::replaceUnknown(const Symbol & symbol) { } int SymbolNode::polynomialDegree(Context & context, const char * symbolName) const { - if (strcmp(m_name,symbolName) == 0) { + if (strcmp(m_name, symbolName) == 0) { return 1; } return 0; @@ -66,11 +67,7 @@ int SymbolNode::getVariables(Context & context, isVariableTest isVariable, char } float SymbolNode::characteristicXRange(Context & context, Preferences::AngleUnit angleUnit) const { - if (m_name[0] == Symbol::SpecialSymbols::UnknownX) { - assert(m_name[1] == 0); - return NAN; - } - return 0.0f; + return isUnknownX() ? NAN : 0.0f; } bool SymbolNode::isReal(Context & context) const { @@ -79,10 +76,10 @@ bool SymbolNode::isReal(Context & context) const { } Layout SymbolNode::createLayout(Preferences::PrintFloatMode floatDisplayMode, int numberOfSignificantDigits) const { - if (m_name[0] == Symbol::SpecialSymbols::UnknownX) { - assert(m_name[1] == 0); + if (isUnknownX()) { return CodePointLayout::Builder(Symbol::k_unknownXReadableChar); } + // TODO return Parse(m_name).createLayout() ? if (strcmp(m_name, "u(n)") == 0) { return HorizontalLayout::Builder( CodePointLayout::Builder('u'), @@ -148,6 +145,14 @@ Expression Symbol::UntypedBuilder(const char * name, size_t length, Context * co return Expression(); } +bool SymbolNode::isUnknownX() const { + bool result = UTF8Helper::CodePointIs(m_name, Symbol::SpecialSymbols::UnknownX); + if (result) { + assert(m_name[1] == 0); + } + return result; +} + bool Symbol::isSeriesSymbol(const char * c) { // [NV][1-3] if (c[2] == 0 && (c[0] == 'N' || c[0] == 'V') && c[1] >= '1' && c[1] <= '3') { diff --git a/poincare/src/symbol_abstract.cpp b/poincare/src/symbol_abstract.cpp index 9e9a518ad..5ac3f5546 100644 --- a/poincare/src/symbol_abstract.cpp +++ b/poincare/src/symbol_abstract.cpp @@ -6,6 +6,8 @@ #include #include #include +#include +#include #include namespace Poincare { @@ -46,13 +48,7 @@ T SymbolAbstract::Builder(const char * name, int length) { } size_t SymbolAbstract::TruncateExtension(char * dst, const char * src, size_t len) { - const char * cur = src; - const char * end = src+len-1; - while (*cur != '.' && cur < end) { - *dst++ = *cur++; - } - *dst = 0; - return cur-src; + return UTF8Helper::CopyUntilCodePoint(dst, len, src, '.'); } bool SymbolAbstract::matches(const SymbolAbstract & symbol, ExpressionTest test, Context & context) { diff --git a/poincare/src/vertical_offset_layout.cpp b/poincare/src/vertical_offset_layout.cpp index efc4d4e50..627e15af7 100644 --- a/poincare/src/vertical_offset_layout.cpp +++ b/poincare/src/vertical_offset_layout.cpp @@ -158,16 +158,16 @@ int VerticalOffsetLayoutNode::serialize(char * buffer, int bufferSize, Preferenc return 0; } // If the layout is a subscript, write "_{indice}" - int numberOfChar = SerializationHelper::Char(buffer, bufferSize, '_'); + int numberOfChar = SerializationHelper::CodePoint(buffer, bufferSize, '_'); if (numberOfChar >= bufferSize-1) { return bufferSize-1; } - numberOfChar += SerializationHelper::Char(buffer+numberOfChar, bufferSize-numberOfChar, '{'); + numberOfChar += SerializationHelper::CodePoint(buffer+numberOfChar, bufferSize-numberOfChar, '{'); if (numberOfChar >= bufferSize-1) { return bufferSize-1; } numberOfChar += const_cast(this)->indiceLayout()->serialize(buffer+numberOfChar, bufferSize-numberOfChar, floatDisplayMode, numberOfSignificantDigits); if (numberOfChar >= bufferSize-1) { return bufferSize-1; } - numberOfChar += SerializationHelper::Char(buffer+numberOfChar, bufferSize-numberOfChar, '}'); + numberOfChar += SerializationHelper::CodePoint(buffer+numberOfChar, bufferSize-numberOfChar, '}'); if (numberOfChar >= bufferSize-1) { return bufferSize-1; } return numberOfChar;